Skip to content

Commit ef7f278

Browse files
Refactor Tic-tac-toe game logic to implement GameInterface and improve GetPlayerMove error handling
1 parent 9e44776 commit ef7f278

2 files changed

Lines changed: 517 additions & 94 deletions

File tree

internal/tictactoe/tictactoe.go

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ type Prompter interface {
1414
Select(prompt, defaultValue string, options []string) (int, error)
1515
}
1616

17+
// GameInterface defines the methods needed for GetPlayerMove
18+
type GameInterface interface {
19+
GetAvailablePositions() []string
20+
}
21+
1722
// Board represents a 3x3 Tic-tac-toe game board.
1823
// Empty squares are represented by empty strings,
1924
// and played squares contain either "X" or "O".
@@ -43,11 +48,9 @@ func (g *Game) MakeMove(rowIndex, columnIndex int) error {
4348
if rowIndex < 0 || rowIndex > 2 || columnIndex < 0 || columnIndex > 2 {
4449
return errors.New("invalid position: must be between 0 and 2")
4550
}
46-
4751
if g.board[rowIndex][columnIndex] != "" {
4852
return errors.New("position already taken")
4953
}
50-
5154
g.board[rowIndex][columnIndex] = g.CurrentPlayer
5255
g.CurrentPlayer = switchPlayer(g.CurrentPlayer)
5356
return nil
@@ -97,6 +100,7 @@ func (g *Game) GetWinner() string {
97100
// IsBoardFull determines if all positions on the board have been played.
98101
// Returns true if no empty positions remain, false otherwise.
99102
func (g *Game) IsBoardFull() bool {
103+
100104
for rowIndex := 0; rowIndex < 3; rowIndex++ {
101105
for columnIndex := 0; columnIndex < 3; columnIndex++ {
102106
if g.board[rowIndex][columnIndex] == "" {
@@ -158,14 +162,18 @@ func switchPlayer(currentPlayer string) string {
158162
// 1 2 3
159163
// 4 5 6
160164
// 7 8 9
165+
// Returns (-1, -1) for invalid positions (0 or >9)
161166
func positionToRowCol(position int) (rowIndex, columnIndex int) {
167+
if position <= 0 || position > 9 {
168+
return -1, -1
169+
}
162170
position-- // Convert to 0-based index
163171
return position / 3, position % 3
164172
}
165173

166-
// getAvailablePositions returns a slice of strings representing unoccupied positions.
174+
// GetAvailablePositions returns a slice of strings representing unoccupied positions.
167175
// The positions are numbered 1-9 (one-based) to match the display format.
168-
func (g *Game) getAvailablePositions() []string {
176+
func (g *Game) GetAvailablePositions() []string {
169177
var availablePositions []string
170178
for position := 1; position <= 9; position++ {
171179
rowIndex, columnIndex := positionToRowCol(position)
@@ -182,27 +190,31 @@ func (g *Game) getAvailablePositions() []string {
182190
// - No valid moves are available (board is full)
183191
// - User input is invalid
184192
// - Selected position is invalid
185-
func GetPlayerMove(prompter Prompter, game *Game) (rowIndex, columnIndex int, err error) {
186-
availablePositions := game.getAvailablePositions()
193+
func GetPlayerMove(prompter Prompter, game GameInterface) (rowIndex, columnIndex int, err error) {
194+
availablePositions := game.GetAvailablePositions()
187195
if len(availablePositions) == 0 {
188-
return 0, 0, errors.New("no available moves")
196+
return -1, -1, errors.New("no available moves")
189197
}
190198

191-
posIndex, err := prompter.Select("Select position (1-9):", availablePositions[0], availablePositions)
199+
posIndex, err := prompter.Select("Select position (1-9):", "1", availablePositions)
192200
if err != nil {
193-
return 0, 0, err
201+
return -1, -1, err
194202
}
195203

196204
if posIndex < 0 || posIndex >= len(availablePositions) {
197-
return 0, 0, fmt.Errorf("invalid position selection: %d", posIndex)
205+
return -1, -1, fmt.Errorf("invalid position selection: %d", posIndex)
198206
}
199207

200208
var position int
201209
_, err = fmt.Sscanf(availablePositions[posIndex], "%d", &position)
202210
if err != nil {
203-
return 0, 0, fmt.Errorf("invalid position value: %v", err)
211+
return -1, -1, fmt.Errorf("invalid position value: %v", err)
204212
}
205213

206214
rowIndex, columnIndex = positionToRowCol(position)
215+
if rowIndex < 0 || columnIndex < 0 {
216+
return -1, -1, fmt.Errorf("invalid position value: %d", position)
217+
}
218+
207219
return rowIndex, columnIndex, nil
208220
}

0 commit comments

Comments
 (0)