From 4f908c24fa4e9acd7bc9c3bb522cdcd73f24a60c Mon Sep 17 00:00:00 2001 From: Nocturn9x Date: Wed, 1 Mar 2023 10:58:08 +0100 Subject: [PATCH] Minor changes and fixes --- src/TicTacToe/board.nim | 24 +++++++++++++----------- src/TicTacToe/player.nim | 34 +++++++++++++++------------------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/TicTacToe/board.nim b/src/TicTacToe/board.nim index 7d8c73d..34d4aad 100644 --- a/src/TicTacToe/board.nim +++ b/src/TicTacToe/board.nim @@ -16,7 +16,9 @@ import ../util/matrix import../util/multibyte -# import std/random +import std/random + +randomize() export matrix @@ -152,7 +154,7 @@ proc get*(self: TicTacToeGame): GameStatus = # a successful tic tac toe is 5, so we don't # bother checking if there's less than those # in the current board - return + return Playing # Checks the rows for _, row in self.map: if all(row == one): @@ -307,23 +309,23 @@ proc findBest*(tree: Move, map: Matrix[int]): Move = ]# -proc findBest*(tree: Move, map: Matrix[uint8], maximize: bool = true, skip: int = 0): Choice = +proc findBest*(tree: Move, maximize: bool = true, skip: int = 0): Choice = ## Finds the best possible move in the ## given playing field using minimax - ## tree search - var start = tree.find(map) - if start.outcome == WinX: + ## tree search. The first skip best + ## results (default 0) are skipped. + if tree.outcome == WinX: return Choice(move: tree, weight: 10 - tree.depth) - elif start.outcome == WinO: + elif tree.outcome == WinO: return Choice(move: tree, weight: -10 + tree.depth) - elif start.outcome == Draw: + elif tree.outcome == Draw: return Choice(move: tree, weight: 0) var choices: seq[Choice] = @[] for i in 0..8: - if start.next[i].isNil(): + if tree.next[i].isNil(): continue - choices.add(start.next[i].findBest(start.next[i].state, maximize=not maximize)) - choices[^1].move = start.next[i] + choices.add(tree.next[i].findBest(maximize=not maximize)) + choices[^1].move = tree.next[i] var best: Choice var bestWeight: int = 100 var worst: Choice diff --git a/src/TicTacToe/player.nim b/src/TicTacToe/player.nim index a7d9823..daf789c 100644 --- a/src/TicTacToe/player.nim +++ b/src/TicTacToe/player.nim @@ -27,11 +27,15 @@ import std/os import std/exitprocs +template clearScreen = + eraseScreen() + setCursorPos(0, 0) + + proc play(treeA, treeB: Move) = ## Plays a game of tic tac toe ## against the user - eraseScreen() - setCursorPos(0, 0) + clearScreen() var game = newTicTacToe() var moves = treeA var location: tuple[row, col: int] @@ -41,12 +45,10 @@ proc play(treeA, treeB: Move) = moves = treeB location = where(moves.state, moves.state != game.map, 3).index(Self.uint8) game.place(Self, location.row, location.col) - eraseScreen() - setCursorPos(0, 0) + clearScreen() styledEcho fgCyan, styleBright, "Computer chose ", fgYellow, $game.map.getIndex(location.row, location.col) else: - eraseScreen() - setCursorPos(0, 0) + clearScreen() while game.get() == Playing: styledEcho fgBlue, styleBright, "Tic Tac Bot v1.0" echo game, "\n" @@ -57,30 +59,25 @@ proc play(treeA, treeB: Move) = index = int(parseBiggestInt(readLine(stdin).strip(chars={'\n'}))) location = ind2sub(index, game.map.shape) except ValueError: - eraseScreen() - setCursorPos(0, 0) + clearScreen() styledEcho fgRed, styleBright, "Invalid move" continue if index notin 0..8 or TileKind(game.map[location.row, location.col]) != Empty: - eraseScreen() - setCursorPos(0, 0) + clearScreen() styledEcho fgRed, styleBright, "Invalid move" continue game.place(Enemy, location.row, location.col) - eraseScreen() - setCursorPos(0, 0) + clearScreen() if game.get() == WinO: echo game, "\n" styledEcho fgGreen, styleBright, "Human wins!" return elif game.get() == Draw: break - moves = moves.find(game.map) - moves = moves.findBest(game.map, true).move + moves = moves.next[game.map.getIndex(location.row, location.col)].findBest(true).move location = where(moves.state, moves.state != game.map, 3).index(Self.uint8) game.place(Self, location.row, location.col) - eraseScreen() - setCursorPos(0, 0) + clearScreen() if game.get() != Draw: styledEcho fgCyan, styleBright, "Computer chose ", fgYellow, $game.map.getIndex(location.row, location.col) if game.get() == WinX: @@ -88,7 +85,7 @@ proc play(treeA, treeB: Move) = styledEcho fgRed, styleBright, "Computer wins!" return echo game - echo "It's a draw!" + styledEcho fgYellow, styleBright, "It's a draw!" proc hook {.noconv.} = @@ -135,10 +132,9 @@ when isMainModule: fp.close() # Here we pick one of the first 5 best moves so that the bot doesn't # always start with an X in the left corner when it's playing first - var state = movesB.state.copy() var best: seq[Move] = @[] for i in 0..4: - best.add(movesB.findBest(state, true, i).move) + best.add(movesB.findBest(true, i).move) movesB = sample(best) while true: try: