diff --git a/README.md b/README.md index ecd742e..e864f2e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,46 @@ -# casymo +# Casimo + +### About the project +A sophisticated casino simulation built with Scala 3 and Scala.js, featuring realistic customer behavior, multiple casino games, and real-time data visualization. + +### Live demo +A live demo can be found through the [repo GitHub's page](https://nickghignatti.github.io/casimo/) + +### Documentation +All the documentation is accessible at the following link: [Casimo documentation](https://nickghignatti.github.io/casimo/docs) + +### Local dev +Prerequisites +- JDK 17+ +- Node.js 16+ (for Scala.js) +- sbt 1.10.11+ + +```shell +# Clone the repository +git clone https://github.com/nickghignatti/casimo.git +cd casimo + +# Run the application +npm run dev +``` + +### Testing +```shell +# Run all tests +sbt "+backendJVM/test" + +# Run with coverage +sbt "coverage; backendJVM/test; coverageReport" +``` ### Code coverage -[![codecov](https://codecov.io/gh/NickGhignatti/casimo/branch/master/graph/badge.svg)](https://codecov.io/gh/NickGhignatti/casimo) \ No newline at end of file +[![codecov](https://codecov.io/gh/NickGhignatti/casimo/branch/master/graph/badge.svg)](https://codecov.io/gh/NickGhignatti/casimo) + +### License +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +### Authors +- [Marco Galeri](https://github.com/Fre0Grella) +- [Nicolò Ghignatti](https://github.com/NickGhignatti) +- [Luca Patrignani](https://github.com/luca-patrignani) \ No newline at end of file diff --git a/backend/src/main/scala/utils/Result.scala b/backend/src/main/scala/utils/Result.scala index 331f510..402d926 100644 --- a/backend/src/main/scala/utils/Result.scala +++ b/backend/src/main/scala/utils/Result.scala @@ -1,29 +1,110 @@ package utils +/** A type representing either a successful value or an error. + * + * `Result[T, E]` is similar to `Either[E, T]` but with more intuitive naming + * where `T` represents the success type and `E` represents the error type. + * This is commonly used for error handling without exceptions. + * + * @tparam T + * the type of the success value + * @tparam E + * the type of the error value + * @example + * {{{ val success: Result[Int, String] = Result.Success(42) val failure: + * Result[Int, String] = Result.Failure("Something went wrong") + * + * val doubled = success.map(_ * 2) // Result.Success(84) val errorResult = + * failure.map(_ * 2) // Result.Failure("Something went wrong") }}} + */ enum Result[+T, +E]: + /** Represents a successful result containing a value of type `T`. + * + * @param value + * the successful value + */ case Success(value: T) + + /** Represents a failed result containing an error of type `E`. + * + * @param error + * the error value + */ case Failure(error: E) + /** Transforms the success value using the provided function, leaving failures + * unchanged. + * + * @param f + * the function to apply to the success value + * @tparam U + * the type of the transformed value + * @return + * a new Result with the transformed value if this is a Success, or the + * same Failure if this is a Failure + */ def map[U](f: T => U): Result[U, E] = this match case Success(value) => Success(f(value)) case Failure(error) => Failure(error) + /** Applies a function that returns a Result to the success value, flattening + * the result. This is useful for chaining operations that might fail. + * + * @param f + * the function to apply to the success value + * @tparam U + * the success type of the returned Result + * @tparam F + * the error type of the returned Result + * @return + * the result of applying f if this is a Success, or the same Failure if + * this is a Failure + */ def flatMap[U, F](f: T => Result[U, F]): Result[U, E | F] = this match case Success(value) => f(value) case Failure(error) => Failure(error) + /** Returns the success value if this is a Success, otherwise returns the + * default value. + * + * @param default + * the value to return if this is a Failure + * @tparam U + * the type of the default value (must be a supertype of T) + * @return + * the success value or the default value + */ def getOrElse[U >: T](default: U): U = this match case Success(value) => value case Failure(_) => default + /** Returns true if this Result is a Success, false otherwise. + * + * @return + * true if Success, false if Failure + */ def isSuccess: Boolean = this match case Success(_) => true case Failure(_) => false + /** Returns true if this Result is a Failure, false otherwise. + * + * @return + * true if Failure, false if Success + */ def isFailure: Boolean = !isSuccess +/** Companion object for Result containing utility methods and extensions. + */ object Result: extension [A](result: Result[A, A]) + /** Converts a Result where both success and error types are the same to an + * Option. Success values are converted to Some, and Failure values are + * converted to None. + * + * @return + * Some(value) if Success, None if Failure + */ def option(): Option[A] = result match case Result.Success(value) => Some(value) diff --git a/backend/src/main/scala/utils/Vector2D.scala b/backend/src/main/scala/utils/Vector2D.scala index ab001b5..929519e 100644 --- a/backend/src/main/scala/utils/Vector2D.scala +++ b/backend/src/main/scala/utils/Vector2D.scala @@ -2,30 +2,154 @@ package utils import scala.util.Random +/** A 2D vector with double precision coordinates. + * + * Represents a point or direction in 2D space with x and y components. + * Provides common vector operations including arithmetic, dot product, + * magnitude calculation, and normalization. + * + * @param x + * the x-coordinate component + * @param y + * the y-coordinate component + */ case class Vector2D(x: Double, y: Double): + + /** Adds two vectors component-wise. + * + * @param other + * the vector to add + * @return + * a new vector with the sum of corresponding components + */ def +(other: Vector2D): Vector2D = Vector2D(this.x + other.x, this.y + other.y) + + /** Subtracts two vectors component-wise. + * + * @param other + * the vector to subtract + * @return + * a new vector with the difference of corresponding components + */ def -(other: Vector2D): Vector2D = Vector2D(this.x - other.x, this.y - other.y) + + /** Multiplies the vector by a scalar value. + * + * @param scalar + * the scalar value to multiply by + * @return + * a new vector with both components scaled + */ def *(scalar: Double): Vector2D = Vector2D(this.x * scalar, this.y * scalar) + + /** Divides the vector by a scalar value. + * + * @param scalar + * the scalar value to divide by (should not be zero) + * @return + * a new vector with both components divided by the scalar + */ def /(scalar: Double): Vector2D = Vector2D(this.x / scalar, this.y / scalar) + + /** Computes the dot product of this vector with another vector. + * + * The dot product is useful for determining the angle between vectors and + * for projection calculations. + * + * @param other + * the other vector + * @return + * the dot product as a scalar value + */ def dot(other: Vector2D): Double = this.x * other.x + this.y * other.y + + /** Returns the negation of this vector (unary minus operator). + * + * @return + * a new vector with both components negated + */ def unary_- : Vector2D = Vector2D(-this.x, -this.y) + /** Calculates the magnitude (length) of this vector. + * + * Uses the Euclidean distance formula: √(x² + y²) + * + * @return + * the magnitude as a non-negative double + */ def magnitude: Double = Math.sqrt(x * x + y * y) + + /** Returns a normalized version of this vector (unit vector). + * + * A normalized vector has magnitude 1.0 and points in the same direction. If + * this vector has zero magnitude, returns the same vector unchanged. + * + * @return + * a new vector with magnitude 1.0, or the original vector if magnitude is + * 0 + */ def normalize: Vector2D = if (magnitude == 0.0) this else this / magnitude + /** Generates a random vector within a square area around this vector. + * + * Creates a new vector where each component is randomly chosen within the + * range [this.component - radius, this.component + radius]. + * + * @param radius + * the maximum distance from this vector's components + * @return + * a new random vector within the specified area + */ def around(radius: Double): Vector2D = Vector2D( Random.between(this.x - radius, this.x + radius), Random.between(this.y - radius, this.y + radius) ) +/** Companion object for Vector2D containing utility methods and constants. + */ object Vector2D: + /** The zero vector (origin point). + * + * A constant representing the vector with both components equal to 0.0. + */ val zero: Vector2D = Vector2D(0.0, 0.0) + + /** Creates a zero vector. + * + * Alternative constructor that returns the zero vector without parameters. + * + * @return + * the zero vector Vector2D(0.0, 0.0) + */ def apply(): Vector2D = zero + /** Calculates the Euclidean distance between two vectors. + * + * Computes the magnitude of the difference vector between the two points. + * + * @param u + * the first vector + * @param v + * the second vector + * @return + * the distance between the vectors as a non-negative double + */ def distance(u: Vector2D, v: Vector2D): Double = (v - u).magnitude + /** Calculates the unit direction vector from one point to another. + * + * Returns a normalized vector pointing from the first vector to the second. + * If the vectors are the same, returns the zero vector. + * + * @param from + * the starting vector + * @param to + * the target vector + * @return + * a unit vector pointing from 'from' to 'to' + */ def direction(from: Vector2D, to: Vector2D): Vector2D = (to - from).normalize