Initial commit
[trapthecap.git] / lib / libpplayer.rb~
1 # == Synopsis
2 #
3 # Library to support a Trap the Cap player that uses potentials to select the
4 # best move
5 #
6 # == Author
7 # Neil Smith
8 #
9 # == Change history
10 # Version 1.1:: 23 April 2008
11
12 require 'libttc'
13 require 'libgenetics'
14
15
16 # Play Trap the Cap by using potential fields. For each possible move,
17 # calculate the field strength and pick the move with the lowest potential
18 class Potential_player
19
20 attr_reader :friend_pull, :enemy_pull, :base_pull,
21 :safe_bonus, :capture_bonus
22
23 def initialize(args, verbose = false)
24 @friend_pull = args[:friend_pull] || 1
25 @enemy_pull = args[:enemy_pull] || 0.5
26 @base_pull = args[:base_pull] || 2
27 @safe_bonus = args[:safe_bonus] || 8
28 @capture_bonus = args[:capture_bonus] || 10
29
30 @verbose = verbose
31 end
32
33 # Find the best move of the possible ones
34 def best_move(game, die_result)
35 me = game.current_player
36 possible_moves = game.possible_moves(die_result, me)
37 scored_moves = possible_moves.collect do |m|
38 begin
39 game.apply_move! m
40 score = score_position(game, me)
41 # game.undo_move!
42 rescue GameWonNotice
43 score = 10000
44 ensure
45 game.undo_move!
46 end
47 puts "#{m} scores #{score}" if @verbose
48 [m, score]
49 end
50 best_move = (scored_moves.max {|a, b| a[1] <=> b[1]})[0]
51 end
52
53 # Calculate the potential score of a position for a given player
54 def score_position(game, player)
55 score = 0
56 game.pieces.each_value do |piece|
57 here = piece.position
58 if piece.colour == player
59 game.pieces.each_value do |other_piece|
60 if other_piece.colour == player
61 score += game.board.distance_between[here.place][other_piece.position.place] * @friend_pull
62 else
63 score += game.board.distance_between[here.place][other_piece.position.place] * @enemy_pull
64 end
65 end
66 score += piece.contains.length * @capture_bonus
67 score += game.board.distance_between[here.place][game.board.positions[player].place] *
68 piece.contains.length * @base_pull
69 elsif here == game.board.positions[player]
70 score += @safe_bonus
71 end
72 end
73 score
74 end
75
76
77 # Convert a player to a bitstring
78 def to_bitstring
79 (@friend_pull * 4).to_i.to_bitstring(4).gray_encode +
80 (@enemy_pull * 4).to_i.to_bitstring(4).gray_encode +
81 (@base_pull * 4).to_i.to_bitstring(4).gray_encode +
82 @safe_bonus.to_bitstring(4).gray_encode +
83 @capture_bonus.to_bitstring(4).gray_encode
84 end
85
86 # Convert a player to a genome
87 def to_genome
88 Genome.new(self.to_bitstring)
89 end
90 end
91
92
93 class Genome
94
95 # Create a potential player from a genome
96 def to_potential_player
97 friend_pull = @genome[0, 4].gray_decode.to_decimal.to_f / 4
98 enemy_pull = @genome[4, 4].gray_decode.to_decimal.to_f / 4
99 base_pull = @genome[8, 4].gray_decode.to_decimal.to_f / 4
100 safe_bonus = @genome[12, 4].gray_decode.to_decimal
101 capture_bonus = @genome[16, 4].gray_decode.to_decimal
102 Potential_player.new({:friend_pull => friend_pull,
103 :enemy_pull => enemy_pull, :base_pull => base_pull,
104 :safe_bonus => safe_bonus, :capture_bonus => capture_bonus})
105 end
106 end