Coder Social home page Coder Social logo

app-rpsosa's People

Contributors

raasoft avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

app-rpsosa's Issues

Step 3

requirements

Use a Move model instead of Strings.

specs

  • convert the user input to a valid Move data type
  • use ADT (Algebraic Data Type, i.e. sealed case objects in Scala) encoding for enum (see https://underscore.io/blog/posts/2014/09/03/enumerations.html)
  • use an Option to signal the parsing can fail
  • refactor the implementation to work with the Move data type

misc

  • The enum encoding uses an ADT (Algebraic Data Type), which in scala is a sealed trait extended by case object (more on the subject here: https://underscore.io/blog/posts/2014/09/03/enumerations.html)
  • we decided to use Option to represent the fact that the parsing of a move can fail. This forces us to handle the case in which the user input is invalid. We use pattern match to see whether we got a valid move or not and then move on.
    • we could’ve used more sophisticated data types for the purposes of validating stuff
    • if you’re super curious see for instance Validated, a data type that allows composing multiple validations on the input, while preserving the failure reasons for each validation.
  • computer move is now chosen amongst a list of valid moves
  • the business logic (inside the pattern match) is now much clearer to read and debug

Solution

buildo/rps@step-2...step-3

Step 6

requirements

In this step will be getting rid of those pesky ~ 😃 We’ll be using wiro to skip the akka http DSL entirely. If you’re new to wiro, read https://blog.buildo.io/http-routes-at-buildo-1424250c41d3.

You can follow tutorial at https://buildo.github.io/wiro/ to get started.

misc

Tips

  • the Game object should be split into a GameApi trait and a GameApiImpl class that implements that trait. This is probably redundant for this small app, but it makes sense in real world apps.

  • wiro requires controller method to have the return a Future[Either[ErrorType, A]] where ErrorType and A are up to you. Since this application is not really asynchronous and it can’t really fail, I suggest you use Throwable as the error type and simply wrap the whole implementation into a Future { ... } block.

    • You will need to provide an ToHttpResponse for Throwable, which will never be used. Just to please the compiler add this before deriveRouter (we’re returning null but it doesn’t matter because it’s never going to be used).

      implicit def throwableResponse: ToHttpResponse[Throwable] = null

  • in order for Future { … } to work, it needs an implicit execution context (you already have it from the previous step. This ExecutionContext needs to come from the Main, so GameApiImpl will take it as a (possibly implicit) parameter. This is the stupidest form of Dependency Injection. It’s so simple and stupid that we love it 😃

  • One model you had to introduce in the previous step will become useless, can you guess which one?

Solution

buildo/rps@step-5...step-6

Step 5

requirements

Create an HTTP server using akka-http.
You can copy the client from the solution in step-5 and use it to play!

To copy the client do:

  • git checkout step-5 clien``t

To start the client do:

  • cd client
  • yarn install
  • yarn start

specs

    POST /rps/play
    
    Request
    {
      "userMove": "Rock" | "Paper" | "Scissors"
    }
    
    Response
    {
      "userMove": "Rock" | "Paper" | "Scissors",
      "computerMove": "Rock" | "Paper" | "Scissors",
      "result": "Win" | "Lose" | "Draw"
    }

misc

Tips

  • you will need to return the result of a game from the play method, instead of printing
  • for this, a Result type would come in very handy ;)
  • since the API requires "``Rock``" instead of "``0``", it’s convenient to use @enum instead of @indexedEnum for Move and Result
  • to serialize/deserialize the JSON response/request I strongly suggest to use circe auto-derivation module. You will also need the specific integration for circe and akka-http (which can be found here: https://github.com/hseeberger/akka-http-json) and for circe and enumero (under the package circe-enumer``o``-support
    • this part is “boring” so feel free to copy-paste the dependencies from the build.sbt in the solution project and be sure to place these imports in the file where you define the router
      import de.heikoseeberger.akkahttpcirce.ErrorAccumulatingCirceSupport._
      import io.circe.generic.auto._
      import io.buildo.enumero.circe._
    • with these in place, you should be able to use the entity(as[Foo]) directive from akka-http
    • by the way, you will need to define what Foo is 😉 I suggest two case classes response and requests
  • finally, since we don’t to deal with CORS, install https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi/related?hl=en to test it from the browser and be sure to allow any OPTIONS request in your akka-http router. You can do so by appending this to your route definition:
    /* the rest of your router */ ~ options(complete())

Solution

buildo/rps@step-4...step-5

Step 1

First working implementation of the game

requirements

  • User can input “0”, “1”, “2” and play a game of Rock (0) Paper (1) Scissors (2).
  • Create a Game object with a def play(): Unit method which asks for user input and prints the outcome of the game, including the two moves (user’s move and computer’s move)
  • The computer’s move is generate at random

misc

Relevant points from the solution

  • we’re using string interpolation with s``""
  • we’re using Strings directly
  • we’re printing the result directly from the implementation of play, which in turn returns Unit

Solution

buildo/rps@step-0...step-1

Step 7

requirements

In this step we’ll modify the endpoint to be CQS compliant (+Frontend API guidelines ).

Since we need to modify the actual http call we need to update the client, don’t worry you can do it with the following command

git checkout step-7 client

The new API is split in two routes: one for making a move and one for getting the game result:

    POST /rps/play
    
    Request
    {
      "userMove": "Rock" | "Paper" | "Scissors"
    }
    
    Response
    200 OK
    
    GET /rps/result
    
    Response
    {
      "userMove": "Rock" | "Paper" | "Scissors",
      "computerMove": "Rock" | "Paper" | "Scissors",
      "result": "Win" | "Lose" | "Draw"
    }

The game result must be first computed and then stored in memory for later retrieval by the frontend. To accomplish this you can use, for example, java.util.concurrent.atomic.AtomicReference or scala.collection.concurrent.TrieMap, or any other data structure that supports concurrent reads and writes.

Instead of adding more code to the GameApi, try to refactor the code into three logical layers, each handling a different concern (+Backend design guidelines):

  • Controller: routing logic (mostly wiro annotations and data model conversions)
  • Service: application logic (the computation of the computer move)
  • Repository: persistence logic (where the play result is stored in memory)

Probably these new components will use (depend on) each other like this: GameControllerGameServiceGameRepository. Try to make these dependencies explicit in the components’ constructors, wiring them in Main.

specs

misc

Step 4

requirements

Use enumero to define Move

specs

  • use enumero (https://github.com/buildo/enumero) to encode Move
  • use indexedEnum from enumero, using String as index
  • use CaseEnumSerialization to print to string and CaseEnumIndex to parse from index

misc

Relevant points

  • enumero standardizes the ADT technique we’ve seen above
  • the API is still under development and suggestions are welcome :-)

Solution

buildo/rps@step-3...step-4

Step 2

requirements

  • In case you used an if or a nested match in Step 1, refactor your code to use a single match on a tuple (userMove, computerMove)

specifications

  • Minor refactor, using pattern matching instead of an if/else

misc

Relevant points from the solution

  • we’re creating a tuple to match on → this makes the pattern match very convenient
  • we’re using a “guard” to ensure x and y are equal in the Draw case
    • if you’re thinking whether case (x, x) would’ve worked, nope, it doesn’t, sorry. (It works in Prolog though!)
  • case _ as the default case → this makes the pattern match complete (i.e. it covers all the cases), which is always a good idea
  • using “0”, “1”, “2” still sucks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.