Compare commits

..

5 Commits

Author SHA1 Message Date
Dan Ballard d08af5722c better integrate ai and play function. it works for moves. not so much
with jumps
2011-09-13 23:33:51 -07:00
Dan Ballard 96b3abbfa1 Merge branch 'master' into pruning
Conflicts:
	checkers.rb
2011-09-13 21:55:41 -07:00
Dan Ballard da4ca314b8 slightly ok pruning based on max score of other branches of DFS and
modifying depth of branch by pieces taken
2011-09-12 22:51:43 -07:00
Dan Ballard 78afdda3f0 doc changes to search function 2011-09-12 18:08:23 -07:00
Dan Ballard 34bccf463f adding some max pruning... not working past depth 25? 2011-09-12 13:44:19 -07:00
2 changed files with 81 additions and 48 deletions

8
README
View File

@ -1,17 +1,13 @@
Checkers AI engine Checkers AI engine
For Ruby 1.9.1
Right now I'm working on the search. Right now I'm working on the search.
To test To test
% load "checkers.rb" % load "checkers.rb"
% b = Board.new % b = Board.new
% b.play % b.setup
also look at and then to look
% b.search(TEAM_1) % b.search(TEAM_1)
Right now entering jump moves or the AI playing jumps in the .play() interface may not work

View File

@ -19,6 +19,7 @@ class Board
end end
def setup() def setup()
initialize()
0.step(BOARD_SIZE) {|i| 0.step(BOARD_SIZE) {|i|
peice = (i < 3 ? TEAM_1_MAN : TEAM_2_MAN) peice = (i < 3 ? TEAM_1_MAN : TEAM_2_MAN)
if [0,2,6].include? i if [0,2,6].include? i
@ -226,7 +227,8 @@ class Board
moves.each { |move| moves.each { |move|
board = dup() board = dup()
board.do_move(x, y, move, team(x, y)) board.do_move(x, y, move, team(x, y))
boards.push(board) boards.push( { "board" => board, "move" =>
{'from' => [x,y], 'to' => move}})
} }
end end
end end
@ -234,66 +236,91 @@ class Board
return boards return boards
end end
def score(team) def score()
score = 0 scores = {}
score += @stats[team]['count'] [TEAM_1, TEAM_2].each {|team|
score += 12 - @stats[opposite_team(team)]['count'] score = 0
if @stats[opposite_team(team)]['count'] == 0 score += @stats[team]['count']
score += 12 score += 12 - @stats[opposite_team(team)]['count']
elsif @stats[team]['count'] == 0 if @stats[opposite_team(team)]['count'] == 0
score -= 12 score += 12
end elsif @stats[team]['count'] == 0
return score score -= 12
end
scores[team] = score
}
return scores
end end
def search(team) def search(team)
@top_depth = 10 return search_do(team, team, 0, 4, 100.0, 0.0, @stats.dup, [])
search_do(team, @top_depth, 100.0, 0.0, "R")
end end
def search_do(team, depth, percent, last_percent, location) # the owner of the search: affects pruning bonuses
#puts location # team - current player this turn
if depth == 0 # current depth - counting up to max_depth
#puts "DONE" # max_depth - when we should stop our search
return score(team) # my_percent - the percent range this work covers
end # done_percent - the percent of work already done
max = -100 # stats - stats from 2 turns ago, ie before most recent opponent turn
# maxs - depth indexed array of max scores
def search_do(owner, team, current_depth, max_depth, my_percent, done_percent, stats, maxs)
#puts current_depth.to_s + '/' + max_depth.to_s + ' ' + done_percent.to_s + "%"
if current_depth < maxs.length and score()[team] < maxs[current_depth][team]
return score()
end
if current_depth >= maxs.length or score()[team] > maxs[current_depth][team]
maxs[current_depth] = score()
end
if current_depth >= max_depth
return score()
end
#if team == TEAM_1
# puts depth.to_s + ": TEAM_1's turn:"
#else
# puts depth.to_s + ": TEAM_2's turn:"
#end
#puts to_s #moves[0].to_s
if @stats[TEAM_1]['count'] == 0 if @stats[TEAM_1]['count'] == 0
puts "TEAM_2 WON!" puts "TEAM_2 WON!"
return score(team) return score()
elsif @stats[TEAM_2]['count'] == 0 elsif @stats[TEAM_2]['count'] == 0
puts "TEAM_1 WON!" puts "TEAM_1 WON!"
return score(team) puts to_s
return score()
else else
moves = gen_next_move_boards(team) moves = gen_next_move_boards(team)
if moves == [] if moves == []
puts "NO MOVES AVAILABLE?" puts "NO MOVES AVAILABLE?"
return score(team) return score()
else else
jumps = moves.find_all { |item| item['move'][0].is_a?(Array)}
if jumps.length > 0
moves = jumps
end
done = 0.0 done = 0.0
item_percent = percent/moves.length item_percent = my_percent/moves.length
max = nil
for i in 0..moves.length-1 do for i in 0..moves.length-1 do
#moves.each { |move| depth_mod = 0
move = moves[i] move = moves[i]['board']
#puts "SEARCH" # modify depth on piece loss
sub_score = move.search_do(opposite_team(team), depth -1, item_percent, last_percent+done, location + "." +i.to_s ) if stats[team]['count'] > move.stats[team]['count']
# search less is search owner lost peices
#if owner == team
# depth_mod = -4
#else
# depth_mod = 4
#end
end
sub_score = move.search_do(owner, opposite_team(team), current_depth + 1, max_depth + depth_mod, item_percent, done_percent+done, @stats.dup, maxs)
done += item_percent done += item_percent
if done >= 0.0001 if done >= 10.0
last_percent += done; done_percent += done;
puts "%.5f" % last_percent + "% depth: " + depth.to_s + " max: " + max.to_s puts "%.2f" % done_percent + "% depth: " + current_depth.to_s + "/" + max_depth.to_s + " max: " + maxs.to_s
done = 0.0 done = 0.0
end end
#puts "Score: " + sub_score.to_s #puts "Score: " + sub_score.to_s
if sub_score > max if max == nil or sub_score[team] > max[team]
max = sub_score max = sub_score
max['move'] = moves[i]['move']
end end
end end
return max return max
@ -325,7 +352,7 @@ class Board
# todo: deal with loss when no move avail # todo: deal with loss when no move avail
def play() def play()
setup() #setup()
color = '' color = ''
while color != 'w' and color != 'b' while color != 'w' and color != 'b'
print "Choose color ([W]hite or [B]lack): " print "Choose color ([W]hite or [B]lack): "
@ -363,8 +390,11 @@ class Board
print "to: " print "to: "
to = gets().strip! to = gets().strip!
from = parse_coords(from) from = parse_coords(from)
to = parse_coords(to) to = to.split(",").map {|c| parse_coords(c.strip)}
puts to.to_s + " or " + from.to_s if to.length == 1
to = to[0]
end
puts "from "+ from.to_s + " to " + to.to_s
if !to or !from if !to or !from
next next
end end
@ -379,6 +409,13 @@ class Board
do_move(from[0], from[1], to, team) do_move(from[0], from[1], to, team)
else else
puts "AI MOVE" puts "AI MOVE"
info = search(team)
move = info['move']
if move == nil
puts "NO MOVE!!!"
else
do_move(move['from'][0], move['from'][1], move['to'], team(move['from'][0], move['from'][1]))
end
end end
if @stats[TEAM_1]['count'] == 0 if @stats[TEAM_1]['count'] == 0