Minor changes and fixes
This commit is contained in:
parent
2da2ebed9a
commit
4f908c24fa
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue