app-rpsosa's People
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 bycase 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
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 aGameApi
trait and aGameApiImpl
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]]
whereErrorType
andA
are up to you. Since this application is not really asynchronous and it can’t really fail, I suggest you useThrowable
as the error type and simply wrap the whole implementation into aFuture { ... }
block.-
You will need to provide an
ToHttpResponse
forThrowable
, which will never be used. Just to please the compiler add this beforederiveRouter
(we’re returningnull
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 theMain
, soGameApiImpl
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
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
- the provided client should work with your server (running on port 8080)
- use
akka-http
and its routing DSL (https://doc.akka.io/docs/akka-http/current/scala/http/introduction.html#routing-dsl-for-http-servers)
conform to this API - the API consists of a single
POST
call:
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
forMove
andResult
- 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 packagecirce-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
- this part is “boring” so feel free to copy-paste the dependencies from the
- 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
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 adef 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 returnsUnit
Solution
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: GameController
→ GameService
→ GameRepository
. 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, usingString
as index - use
CaseEnumSerialization
to print to string andCaseEnumIndex
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
Step 2
requirements
- In case you used an
if
or a nestedmatch
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
andy
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!)
- if you’re thinking whether
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.