@@ -2,22 +2,26 @@ package com.us.dsb.explore.algs.ttt.manual
22
33import cats .syntax .option ._
44import cats .syntax .either ._
5- import enumeratum .EnumEntry
5+ import enumeratum .{ Enum , EnumEntry }
66
77import scala .annotation .tailrec
88
9+ // ?? any substantial benefit to moving internal methods to class? we could
10+ // avoid some state passing, but only by mutating top-level state member
911
10- // ???? object -> class?
1112/** TTT UI controller. */
1213object GameUI {
1314
1415 // ??? enhance; maybe just put clean strings in; maybe build on GameResult (plus quit case)
1516 case class GameUIResult (text : String )
1617
18+ // ("extends EnumEntry" gets .entryName, enables Enum; "extends Enum[...]"
19+ // enables (and requires) .values.
1720
18- sealed trait UICommand extends EnumEntry
19- object UICommand {
20- // ?? why doesn't UICommand's "sealed" obviate the following one (for exhaustive-match checks?)
21+ private sealed trait UICommand
22+ private object UICommand {
23+ // (Q: Why doesn't UICommand's "sealed" obviate the following one (for
24+ // exhaustive-match checks)?
2125 sealed trait UIMoveCommand extends UICommand
2226 case object Up extends UIMoveCommand
2327 case object Down extends UIMoveCommand
@@ -26,8 +30,15 @@ object GameUI {
2630 case object Mark extends UICommand
2731 case object Quit extends UICommand
2832 }
33+ // ?? Decide "UICommand._" re little scala.Right ~clashes.
2934
30- def parseCommand (rawCmd : String ): Either [String , UICommand ] = {
35+ // (Could put strings in enumerators and use Enum.withName to factor down
36+ // parse function, but then layers wouldn't be separated.)
37+
38+ // ?? revisit String (but may be fine since dealing with input _strings_
39+ // from _user_
40+ // ?? revisit Either--use something fancier (MonadError)?
41+ private def parseCommand (rawCmd : String ): Either [String , UICommand ] = {
3142 import UICommand ._
3243 rawCmd match {
3344 case " u" => Up .asRight
@@ -42,11 +53,13 @@ object GameUI {
4253 }
4354
4455 @ tailrec
45- def getCommand (player : Player ): UICommand = {
46- // ?? clean embedded reference to stdin/console and stdout
56+ private def getCommand (player : Player ): UICommand = {
57+
58+ // ?? clean embedded references to stdin/console and stdout
4759 print(s " Player $player command?: " )
4860 val rawCmd = scala.io.StdIn .readLine()
4961
62+ import scala .Left
5063 parseCommand(rawCmd) match {
5164 case Right (cmd) => cmd
5265 case Left (msg) =>
@@ -55,9 +68,9 @@ object GameUI {
5568 }
5669 }
5770
58- // import UICommand.UIMoveCommand
59- def moveSelection ( uiState : GameUIState ,
60- moveCommand : UICommand . UIMoveCommand ): GameUIState = {
71+ private def moveSelection ( uiState : GameUIState ,
72+ moveCommand : UICommand . UIMoveCommand
73+ ): GameUIState = {
6174 import UICommand ._
6275 moveCommand match {
6376 case Up => uiState.withRowAdustedBy(- 1 )
@@ -68,34 +81,30 @@ object GameUI {
6881 }
6982
7083 // ?? "place mark"?
71- def markAtSelection (uiState : GameUIState ): GameUIState = {
84+ private def markAtSelection (uiState : GameUIState ): GameUIState = {
7285 val moveResult = uiState.gameState.tryMoveAt(uiState.selectedRow,
7386 uiState.selectedColumn)
7487 moveResult match {
7588 case Right (newGameState) =>
7689 uiState.copy(gameState = newGameState)
7790 case Left (errorMsg) =>
78- // ?? clean I/O? add to result and hjave cmd loop show? call ~injected error reporter?
91+ // ?? clean I/O? add to result and have cmd loop show? call ~injected error reporter?
7992 println(errorMsg)
8093 uiState // no change
8194 }
8295 }
8396
84- def doQuit (uiState : GameUIState ): GameUIResult = {
97+ private def doQuit (uiState : GameUIState ): GameUIResult = {
8598 GameUIResult (" Game was quit" )
8699 }
87100
88-
89-
90-
91-
92101 // ?? clean looping more (was while mess, now recursive; is there better Scala way?)
93102 /**
94103 * Logically, loops on prompting for and executing user UI ~commands until
95104 * game over or quit.
96105 */
97106 @ tailrec
98- def getAndDoUiCommands (uiState : GameUIState ): GameUIResult = {
107+ private def getAndDoUiCommands (uiState : GameUIState ): GameUIResult = {
99108 println()
100109 println(uiState.toDisplayString)
101110
@@ -107,34 +116,30 @@ object GameUI {
107116 case Quit =>
108117 doQuit(uiState)
109118 case move : UIMoveCommand => // any move-selection command
110- getAndDoUiCommands(moveSelection(uiState, move))
119+ val nextState = moveSelection(uiState, move)
120+ getAndDoUiCommands(nextState)
111121 case Mark =>
112- val newUiState = markAtSelection(uiState)
113- newUiState .gameState.gameResult match {
122+ val nextState = markAtSelection(uiState)
123+ nextState .gameState.gameResult match {
114124 case None => // game not done yet
115- getAndDoUiCommands(newUiState )
125+ getAndDoUiCommands(nextState )
116126 case Some (gameResult) =>
117-
118- import GameState .GameResult ._
127+ import GameState .GameResult ._ // ???
119128 val textResult =
120129 gameResult match {
121130 case Draw => " Game ended in draw"
122131 case Win (player) => s " Player $player won "
123132 }
124- GameUIResult (textResult) // ?? refine from text
133+ GameUIResult (textResult) // ?? refine from text?
125134 }
126135 }
127136 }
128137
129138 def runGame (): GameUIResult = {
130- // ???? construct GameUIState
131- // ???? _maybe_ move GamUIeState into GameUI and not pass around methods
132139 val initialState =
133- // ?? maybe clean getting indices; maybe get from index ranges, not
134- // constructing here (though here exercises refined type_)
135140 GameUIState (GameState .initial, RowIndex (Index (1 )), ColumnIndex (Index (1 )))
136141
137- GameUI . getAndDoUiCommands(initialState)
142+ getAndDoUiCommands(initialState)
138143
139144 }
140145
0 commit comments