Invincible bot actually works
This commit is contained in:
parent
9376c1f4fb
commit
33d73dbf7b
68
board.rb
Normal file
68
board.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
class Array
|
||||
def same_values?
|
||||
self.uniq.length == 1
|
||||
end
|
||||
|
||||
def contains? x
|
||||
self.include? x
|
||||
end
|
||||
end
|
||||
|
||||
class Board
|
||||
|
||||
attr_reader :spaces, :def_val
|
||||
|
||||
def initialize
|
||||
@def_val = "---"
|
||||
@spaces = Array.new(9, @def_val)
|
||||
end
|
||||
|
||||
def to_s
|
||||
"|#{@spaces[0]}|#{@spaces[1]}|#{@spaces[2]}|
|
||||
-------------
|
||||
|#{@spaces[3]}|#{@spaces[4]}|#{@spaces[5]}|
|
||||
-------------
|
||||
|#{@spaces[6]}|#{@spaces[7]}|#{@spaces[8]}|
|
||||
-------------"
|
||||
end
|
||||
|
||||
def get_free
|
||||
@spaces.each_index.select { |s| @spaces[s] == @def_val }
|
||||
end
|
||||
|
||||
def set val, idx
|
||||
@spaces[idx] = val
|
||||
end
|
||||
|
||||
def remove idx
|
||||
@spaces[idx] = @def_val
|
||||
end
|
||||
|
||||
def any_winner?
|
||||
winning_combos = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
|
||||
winning_combos.each do |s|
|
||||
tmp = s.map {|i| @spaces[i]}
|
||||
if (not tmp.include? @def_val) and tmp.same_values? then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def is_winner? player
|
||||
winning_combos = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
|
||||
winning_combos.each do |s|
|
||||
tmp = s.map {|i| @spaces[i]}
|
||||
if (not tmp.include? @def_val) and tmp.same_values? and tmp[0] == player then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def is_full?
|
||||
get_free.empty?
|
||||
end
|
||||
|
||||
|
||||
end
|
67
bot.rb
Normal file
67
bot.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
class Bot
|
||||
|
||||
attr_reader :best_choice
|
||||
|
||||
def initialize board, piece
|
||||
@piece = piece
|
||||
@opponent = switch piece
|
||||
end
|
||||
|
||||
def move board
|
||||
return "Game is done!" if board.is_full?
|
||||
return "Someone won!" if board.any_winner?
|
||||
# move = @board.get_free.sample
|
||||
# @board.set " O ", move
|
||||
minmax board, @piece
|
||||
board.set @piece, @best_choice
|
||||
end
|
||||
|
||||
|
||||
def minmax board, cur_player
|
||||
return score(board) if game_done?(board)
|
||||
|
||||
scores = {}
|
||||
|
||||
board.get_free.each do |space|
|
||||
# Copy board so we don't mess up original
|
||||
potential_board = board.dup
|
||||
potential_board.set cur_player, space
|
||||
|
||||
|
||||
scores[space] = minmax(potential_board, switch(cur_player))
|
||||
potential_board.remove space
|
||||
end
|
||||
|
||||
@best_choice, best_score = best_choice cur_player, scores
|
||||
best_score
|
||||
end
|
||||
|
||||
def switch cur
|
||||
cur == " X " ? " O " : " X "
|
||||
end
|
||||
|
||||
def game_done? board
|
||||
board.any_winner? || board.is_full?
|
||||
end
|
||||
|
||||
def best_choice piece, scores
|
||||
if piece == @piece then
|
||||
#p scores
|
||||
scores.max_by { |_k, v| v}
|
||||
else
|
||||
scores.min_by { |_k, v| v}
|
||||
end
|
||||
end
|
||||
|
||||
def score board
|
||||
#p @piece
|
||||
#p @opponent
|
||||
if board.is_winner? @piece
|
||||
return 10
|
||||
elsif board.is_winner? @opponent
|
||||
return -10
|
||||
end
|
||||
0
|
||||
end
|
||||
|
||||
end
|
21
human.rb
Normal file
21
human.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
class Human
|
||||
|
||||
attr_reader :piece
|
||||
|
||||
def initialize board, piece
|
||||
@board = board
|
||||
@piece = piece
|
||||
end
|
||||
|
||||
def move
|
||||
print "\nPick a move!\n"
|
||||
free_moves = @board.get_free
|
||||
idx = gets.strip.to_i
|
||||
if not free_moves.include? idx then
|
||||
print "Square is already taken, try again!"
|
||||
move
|
||||
else
|
||||
@board.set @piece, idx
|
||||
end
|
||||
end
|
||||
end
|
151
ticcy.rb
151
ticcy.rb
|
@ -1,130 +1,47 @@
|
|||
# coding: utf-8
|
||||
|
||||
DEF = " "
|
||||
PLAYS = [DEF, DEF, DEF, DEF, DEF, DEF, DEF, DEF, DEF]
|
||||
SCENARIOS = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
|
||||
COM = "COM"
|
||||
COM_VAL = " O "
|
||||
PLAYER = "PLAYER"
|
||||
GAME_DONE = false
|
||||
PLAYER_VAL = " X "
|
||||
require './board'
|
||||
require './bot'
|
||||
require './human'
|
||||
|
||||
@board = Board.new
|
||||
|
||||
|
||||
class Array
|
||||
def same_values?
|
||||
self.uniq.length == 1
|
||||
end
|
||||
end
|
||||
|
||||
def check_win board, player
|
||||
cur_play = player == PLAYER ? PLAYER_VAL : COM_VAL
|
||||
|
||||
SCENARIOS.each do |s|
|
||||
tmp = s.map {|i| board[i]}
|
||||
# p board
|
||||
if (not tmp.include? " ") and tmp.same_values? and tmp[0] == cur_play then
|
||||
# p tmp
|
||||
# p SCENARIOS
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def check_full board
|
||||
if not board.include? " " then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def draw_board board
|
||||
return "
|
||||
|#{board[0]}|#{board[1]}|#{board[2]}|
|
||||
-------------
|
||||
|#{board[3]}|#{board[4]}|#{board[5]}|
|
||||
-------------
|
||||
|#{board[6]}|#{board[7]}|#{board[8]}|
|
||||
-------------"
|
||||
end
|
||||
|
||||
def set player, idx
|
||||
if player == PLAYER
|
||||
PLAYS[idx] = PLAYER_VAL
|
||||
elsif
|
||||
PLAYS[idx] = COM_VAL
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def ai_turn board, player, depth=0
|
||||
if (check_win board, player == PLAYER ? COM : PLAYER) then
|
||||
return -10 + depth
|
||||
end
|
||||
|
||||
if check_full board then
|
||||
return 0
|
||||
end
|
||||
|
||||
max_seen = -(Float::INFINITY)
|
||||
index = 0
|
||||
value = player == PLAYER ? PLAYER_VAL : COM_VAL
|
||||
|
||||
(0...9).each do |x|
|
||||
if board[x] == DEF then
|
||||
new_board = board.dup
|
||||
new_board[x] = value
|
||||
lol = player == PLAYER ? COM : PLAYER
|
||||
move_eval = -(ai_turn new_board, lol, depth+1)
|
||||
|
||||
if move_eval > max_seen then
|
||||
# p [move_eval, x]
|
||||
max_seen = move_eval
|
||||
index = x
|
||||
end
|
||||
end
|
||||
end
|
||||
if depth == 0 then
|
||||
set COM, index
|
||||
end
|
||||
return max_seen
|
||||
end
|
||||
|
||||
|
||||
def play_game
|
||||
def play_game player, bot
|
||||
while true do
|
||||
# system "clear"
|
||||
|
||||
# if (check_win PLAYS, PLAYER) == true then
|
||||
# print "We have a winner!\n"
|
||||
# break
|
||||
# end
|
||||
# if check_full board then
|
||||
# print "Game over!\n"
|
||||
# break
|
||||
# end
|
||||
print @board.to_s
|
||||
player.move
|
||||
|
||||
print draw_board PLAYS
|
||||
puts "\nPick one between 0-8"
|
||||
choice = gets
|
||||
|
||||
if PLAYS[choice.to_i] != " " then
|
||||
print "Space is already taken"
|
||||
else
|
||||
set PLAYER, choice.to_i
|
||||
ai_turn PLAYS, COM
|
||||
if @board.is_full? then
|
||||
print @board.to_s
|
||||
print "Game is done!\n"
|
||||
break
|
||||
end
|
||||
|
||||
if @board.any_winner? then
|
||||
print @board.to_s
|
||||
print "Someone won!\n"
|
||||
break
|
||||
end
|
||||
|
||||
bot.move @board
|
||||
|
||||
if @board.is_full? then
|
||||
print @board.to_s
|
||||
print "Game is done!\n"
|
||||
break
|
||||
end
|
||||
|
||||
if @board.any_winner? then
|
||||
print @board.to_s
|
||||
print "Someone won!\n"
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
play_game
|
||||
player = Human.new @board, " X "
|
||||
bot = Bot.new @board, " O "
|
||||
play_game player, bot
|
||||
|
|
Loading…
Reference in New Issue
Block a user