Skip to content

Commit 74cc072

Browse files
author
Daniel Barclay
committed
ManualTicTacToe: Pulled out UserTextIO.
1 parent 04af7c9 commit 74cc072

File tree

2 files changed

+47
-19
lines changed

2 files changed

+47
-19
lines changed

src/main/scala/com/us/dsb/explore/algs/ttt/manual/GameUI.scala

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,19 @@ object GameUI {
5353
}
5454

5555
@tailrec
56-
private def getCommand(player: Player): UICommand = {
56+
private def getCommand(io: UserTextIO, player: Player): UICommand = {
5757

5858
// ?? clean embedded references to stdin/console and stdout
59-
print(s"Player $player command?: ")
60-
val rawCmd = scala.io.StdIn.readLine()
59+
io.print(s"Player $player command?: ")
60+
61+
val rawCmd = io.readLine()
6162

6263
import scala.Left
6364
parseCommand(rawCmd) match {
6465
case Right(cmd) => cmd
6566
case Left(msg) =>
66-
println(msg)
67-
getCommand(player) // loop
67+
io.println(msg)
68+
getCommand(io, player) // loop
6869
}
6970
}
7071

@@ -81,15 +82,15 @@ object GameUI {
8182
}
8283

8384
// ?? "place mark"?
84-
private def markAtSelection(uiState: GameUIState): GameUIState = {
85+
private def markAtSelection(io: UserTextIO, uiState: GameUIState): GameUIState = {
8586
val moveResult = uiState.gameState.tryMoveAt(uiState.selectedRow,
8687
uiState.selectedColumn)
8788
moveResult match {
8889
case Right(newGameState) =>
8990
uiState.copy(gameState = newGameState)
9091
case Left(errorMsg) =>
91-
// ?? clean I/O? add to result and have cmd loop show? call ~injected error reporter?
92-
println(errorMsg)
92+
// ?? add to result and have command ~loop show?:
93+
io.println(errorMsg)
9394
uiState // no change
9495
}
9596
}
@@ -104,11 +105,11 @@ object GameUI {
104105
* game over or quit.
105106
*/
106107
@tailrec
107-
private def getAndDoUiCommands(uiState: GameUIState): GameUIResult = {
108-
println()
109-
println(uiState.toDisplayString)
108+
private def getAndDoUiCommands(io: UserTextIO, uiState: GameUIState): GameUIResult = {
109+
io.println()
110+
io.println(uiState.toDisplayString)
110111

111-
val command = getCommand(uiState.gameState.currentPlayer)
112+
val command = getCommand(io, uiState.gameState.currentPlayer)
112113

113114
import UICommand._
114115
command match {
@@ -117,12 +118,12 @@ object GameUI {
117118
doQuit(uiState)
118119
case move: UIMoveCommand => // any move-selection command
119120
val nextState = moveSelection(uiState, move)
120-
getAndDoUiCommands(nextState)
121+
getAndDoUiCommands(io, nextState) // loop
121122
case Mark =>
122-
val nextState = markAtSelection(uiState)
123+
val nextState = markAtSelection(io, uiState)
123124
nextState.gameState.gameResult match {
124125
case None => // game not done yet
125-
getAndDoUiCommands(nextState)
126+
getAndDoUiCommands(io, nextState) // loop
126127
case Some(gameResult) =>
127128
import GameState.GameResult._ // ???
128129
val textResult =
@@ -135,11 +136,36 @@ object GameUI {
135136
}
136137
}
137138

138-
def runGame(): GameUIResult = {
139+
// ?? revisit name
140+
trait UserTextIO {
141+
def print(lineOrPart: String): Unit
142+
def println(): Unit = println("")
143+
def println(fullLine: String): Unit
144+
def readLine(): String
145+
146+
// ??? soon, try with separate methods for prompt vs. error (etc.) lines
147+
// (imagine highlighting errors, using bold for actual prompt line
148+
// after plain lines for board rendering)
149+
}
150+
151+
object ConsoleUserTextIO extends UserTextIO {
152+
def print(lineOrPart: String): Unit = Predef.print(lineOrPart)
153+
def println(fullLine: String): Unit = Predef.println(fullLine)
154+
def readLine(): String = scala.io.StdIn.readLine()
155+
}
156+
157+
158+
// ???? next, probably create class GameUI to hold NameThisIO (to avoid passing all around)
159+
// ???? add GameUI tests:
160+
// - 1: driving from outside to normal insides--mocking/etc. UserTextIO?
161+
// - 2: driving from outside to special GameState (test double; spy/reporter/?)
162+
163+
164+
def runGame(io: UserTextIO): GameUIResult = {
139165
val initialState =
140166
GameUIState(GameState.initial, RowIndex(Index(1)), ColumnIndex(Index(1)))
141167

142-
getAndDoUiCommands(initialState)
168+
getAndDoUiCommands(io, initialState)
143169

144170
}
145171

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.us.dsb.explore.algs.ttt.manual
22

33
object ManualTicTacToe extends App {
4-
val gameResult2 = GameUI.runGame() // ?? specify starting user?
5-
println("Result: " + gameResult2.text)
4+
5+
val gameResult = GameUI.runGame(GameUI.ConsoleUserTextIO) // ?? specify starting user?
6+
println("Game result: " + gameResult.text)
7+
68
}

0 commit comments

Comments
 (0)