checkers_game_engine_sgallagher 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/draughts.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'draughts/board'
2
+ require 'draughts/board_survey'
3
+ require 'draughts/checker'
4
+ require 'draughts/game'
5
+ require 'draughts/gui'
6
+ require 'draughts/move_check'
7
+ require 'draughts/user_input'
8
+ require 'draughts/computer_player'
9
+ require 'draughts/evaluation'
10
+ require 'draughts/minimax'
@@ -0,0 +1,98 @@
1
+ class Board
2
+
3
+ def create_board
4
+ board = Array.new(8)
5
+ 8.times { |index| board[index] = Array.new(8) }
6
+ populate_checkers(board)
7
+ board
8
+ end
9
+
10
+ def create_test_board
11
+ board = Array.new(8)
12
+ 8.times { |index| board[index] = Array.new(8) }
13
+ board
14
+ end
15
+
16
+ def add_checker(board, color, x, y)
17
+ board[x][y] = Checker.new(x, y, color)
18
+ end
19
+
20
+ def place_checker_on_board(board, checker)
21
+ board[checker.x_pos][checker.y_pos] = checker
22
+ end
23
+
24
+ def populate_checkers(board)
25
+ 0.upto(2) do |x_coord|
26
+ populate_checker(board, x_coord, :red)
27
+ end
28
+
29
+ 5.upto(7) do |x_coord|
30
+ populate_checker(board, x_coord, :black)
31
+ end
32
+ board
33
+ end
34
+
35
+ def populate_checker(board, x_coord, color)
36
+ evens = [0, 2, 4, 6]
37
+ odds = [1, 3, 5, 7]
38
+
39
+ apply_checker = lambda do |y_coord|
40
+ checker = Checker.new(x_coord, y_coord, color)
41
+ board[x_coord][y_coord] = checker
42
+ end
43
+
44
+ if x_coord.even?
45
+ evens.each(&apply_checker)
46
+ elsif x_coord.odd?
47
+ odds.each(&apply_checker)
48
+ end
49
+ end
50
+
51
+ def checkers_left(board, color)
52
+ checker_count = 0
53
+
54
+ board.each do |row|
55
+ row.each do |location|
56
+ if (location.nil? == false) and (location.color == color)
57
+ checker_count += 1
58
+ end
59
+ end
60
+ end
61
+ checker_count
62
+ end
63
+
64
+ def self.king_checkers_if_necessary(board)
65
+ board.each do |row|
66
+ row.each do |loc|
67
+ if (loc != nil) and (loc.color == :red) and (loc.x_pos == 7)
68
+ loc.make_king
69
+ elsif (loc != nil) and (loc.color == :black) and (loc.x_pos == 0)
70
+ loc.make_king
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ def self.remove_jumped_checker(board, x_origin, y_origin, x_destination, y_destination)
77
+ x_delta = (x_destination > x_origin) ? 1 : -1
78
+ y_delta = (y_destination > y_origin) ? 1 : -1
79
+
80
+ board[x_origin + x_delta][y_origin + y_delta] = nil
81
+ board
82
+ end
83
+
84
+ def self.return_jumped_checker(board, x_origin, y_origin, x_destination, y_destination)
85
+ jumping_checker_color = board[x_destination][y_destination].color
86
+ returning_checker_color = jumping_checker_color == :red ? :black : :red
87
+
88
+ x_delta = (x_destination > x_origin) ? 1 : -1
89
+ y_delta = (y_destination > y_origin) ? 1 : -1
90
+
91
+ board[x_origin + x_delta][y_origin + y_delta] = Checker.new(x_origin + x_delta, y_origin + y_delta, returning_checker_color)
92
+ board
93
+ end
94
+
95
+ def remove_checker(board, x, y)
96
+ board[x][y] = nil
97
+ end
98
+ end
@@ -0,0 +1,248 @@
1
+ class BoardSurvey
2
+
3
+ attr_accessor :board, :current_player
4
+
5
+ QUADRANTS = ["upper_left", "upper_right", "lower_left", "lower_right"]
6
+
7
+ def invert_array(array)
8
+ array.map! { |x| -x }
9
+ end
10
+
11
+ def normal_deltas(current_player)
12
+ deltas = [1, 1, 1, -1, -1, 1, -1, -1]
13
+ current_player == :red ? deltas : invert_array(deltas)
14
+ end
15
+
16
+ def edge?(x)
17
+ x == 7 or x == 0
18
+ end
19
+
20
+ def edge_adjust(x, current_player, hash)
21
+ if x == 0
22
+ current_player == :red ? hash.merge({"lower_left" => nil, "lower_right" => nil}) : hash.merge({"upper_left" => nil, "upper_right" => nil})
23
+ else
24
+ current_player == :red ? hash.merge({"upper_left" => nil, "upper_right" => nil}) : hash.merge({"lower_left" => nil, "lower_right" => nil})
25
+ end
26
+ end
27
+
28
+ def deltas_to_board_locations(deltas, x, y)
29
+ board_coords = []
30
+ deltas.each_slice(2) do |slice|
31
+ board_coords << x + slice[0]
32
+ board_coords << y + slice[1]
33
+ end
34
+ board_coords
35
+ end
36
+
37
+ def assign_adjacent_board_coords(current_player, x, y)
38
+ jump_positions = Hash[QUADRANTS.zip(deltas_to_board_locations(normal_deltas(current_player), x, y).each_slice(2))]
39
+ if edge?(x)
40
+ return edge_adjust(x, current_player, jump_positions)
41
+ end
42
+ jump_positions
43
+ end
44
+
45
+ def determine_adjacent_positions_content(board, board_coords)
46
+ adjacent_content = {}
47
+ board_coords.each_pair { |quad, coords| coords.nil? ? adjacent_content[quad] = nil : adjacent_content[quad] = board[coords[0]][coords[1]] }
48
+ adjacent_content
49
+ end
50
+
51
+ def opposing_checker_adjacent(current_player, adjacent_content)
52
+ opposing_checker_adjacent = {}
53
+ adjacent_content.each_pair do |quad, content|
54
+ if content != nil
55
+ content.color != current_player ? opposing_checker_adjacent[quad] = true : opposing_checker_adjacent[quad] = false
56
+ else
57
+ opposing_checker_adjacent[quad] = false
58
+ end
59
+ end
60
+ opposing_checker_adjacent
61
+ end
62
+
63
+ def not_outside_bounds?(x, y)
64
+ move_check = MoveCheck.new
65
+ not move_check.out_of_bounds?(x, y)
66
+ end
67
+
68
+ def jump_possible?(board, deltas)
69
+ (not_outside_bounds?(deltas[0], deltas[1]) and board[deltas[0]][deltas[1]] == nil) ? true : false
70
+ end
71
+
72
+ def delta_translator(current_player, quad, x, y, mag)
73
+ deltas = []
74
+ if current_player == :red
75
+ case quad
76
+ when "upper_left"
77
+ x += mag; y += mag
78
+ when "upper_right"
79
+ x += mag; y -= mag
80
+ when "lower_left"
81
+ x -= mag; y += mag
82
+ when "lower_right"
83
+ x -= mag; y -= mag
84
+ end
85
+ else
86
+ case quad
87
+ when "upper_left"
88
+ x -= mag; y -= mag
89
+ when "upper_right"
90
+ x -= mag; y += mag
91
+ when "lower_left"
92
+ x += mag; y -= mag
93
+ when "lower_right"
94
+ x += mag; y += mag
95
+ end
96
+ end
97
+ deltas << x << y
98
+ deltas
99
+ end
100
+
101
+ def adjust_jump_locations_if_not_king(board, x, y, jump_locations)
102
+ unless board[x][y].is_king?
103
+ jump_locations["lower_left"] = false
104
+ jump_locations["lower_right"] = false
105
+ end
106
+ jump_locations
107
+ end
108
+
109
+ def jump_locations(board, current_player, x, y, opposing_checkers)
110
+
111
+ jump_locations = {}
112
+ opposing_checkers.each_pair do |quad, present|
113
+ if present
114
+ deltas = delta_translator(current_player, quad, x, y, 2)
115
+ jump_possible?(board, deltas) ? jump_locations[quad] = true : jump_locations[quad] = false
116
+ else
117
+ jump_locations[quad] = false
118
+ end
119
+ end
120
+ adjust_jump_locations_if_not_king(board, x, y, jump_locations)
121
+ jump_locations
122
+ end
123
+
124
+ def coordinates_of_jump_landings(current_player,x, y, jump_locations)
125
+ jump_coords = []
126
+
127
+ jump_locations.each_pair do |quad, jump|
128
+ if jump
129
+ jump_coords << delta_translator(current_player, quad, x, y, 2)
130
+ end
131
+ end
132
+ jump_coords
133
+ end
134
+
135
+ def jump_location_finder_stack(board, current_player, x, y)
136
+ adj_board_coords = assign_adjacent_board_coords(current_player, x, y)
137
+ adj_content = determine_adjacent_positions_content(board, adj_board_coords)
138
+ opposing_checkers = opposing_checker_adjacent(current_player, adj_content)
139
+ jump_locations(board, current_player, x, y, opposing_checkers)
140
+ end
141
+
142
+ def generate_jump_locations_list(board, current_player)
143
+ coordinates_list = []
144
+
145
+ board.each do |row|
146
+ row.each do |loc|
147
+ if (loc != nil) and (loc.color == current_player)
148
+ jump_locations = jump_location_finder_stack(board, current_player, loc.x_pos, loc.y_pos)
149
+ coordinates_list << coordinates_of_jump_landings(current_player, loc.x_pos, loc.y_pos, jump_locations)
150
+ end
151
+ end
152
+ end
153
+ coordinates_list.flatten
154
+ end
155
+
156
+ def any_jumps_left?(board, current_player, x, y)
157
+ jumps = jump_location_finder_stack(board, current_player, x, y)
158
+ jumps.has_value?(true)
159
+ end
160
+
161
+ def surrounding_locations_for_checker(board, current_player, x, y)
162
+ #p "BOARDSURVEY::SURROUNDING board position = #{x}, #{y} - #{board[x][y]}"
163
+ deltas = normal_deltas(current_player)
164
+ possible_moves = deltas_to_board_locations(deltas, x, y)
165
+ if board[x][y].is_king? == false
166
+ possible_moves.slice!(4, 4)
167
+ end
168
+ possible_moves
169
+ end
170
+
171
+ def remove_out_of_bounds_locations(possible_moves)
172
+ corrected_possible_moves = []
173
+ possible_moves.each_slice(2) do |coords|
174
+ if ((coords[0] <= 7 and coords[0] >= 0) and (coords[1] <= 7 and coords[1] >= 0))
175
+ corrected_possible_moves << coords[0] << coords[1]
176
+ end
177
+ end
178
+ corrected_possible_moves
179
+ end
180
+
181
+ def normal_move_locations(possible_moves, board, current_player, x, y)
182
+ corrected_possible_moves = []
183
+ possible_moves.each_slice(2) do |coords|
184
+ if board[coords[0]][coords[1]] == nil
185
+ corrected_possible_moves << x << y << coords[0] << coords[1]
186
+ end
187
+ end
188
+ corrected_possible_moves
189
+ end
190
+
191
+ def normal_move_location_finder_stack(board, current_player, x, y)
192
+ locations = surrounding_locations_for_checker(board, current_player, x, y)
193
+ removed_out_of_bounds_locations = remove_out_of_bounds_locations(locations)
194
+ normal_move_locations = normal_move_locations(removed_out_of_bounds_locations, board, current_player, x, y)
195
+ end
196
+
197
+ def generate_normal_move_locations_list(board, current_player)
198
+ move_locations = []
199
+
200
+ board.each do |row|
201
+ row.each do |loc|
202
+ if (loc != nil) and (loc.color == current_player)
203
+ move_locations << normal_move_location_finder_stack(board, current_player, loc.x_pos, loc.y_pos)
204
+ end
205
+ end
206
+ end
207
+ move_locations.flatten
208
+ end
209
+
210
+ def coordinates_of_computer_jump_landings(current_player,x, y, jump_locations)
211
+ jump_coords = []
212
+
213
+ jump_locations.each_pair do |quad, jump|
214
+ if jump
215
+ jump_coords << x << y << delta_translator(current_player, quad, x, y, 2)
216
+ end
217
+ end
218
+ jump_coords
219
+ end
220
+
221
+ def generate_computer_jump_locations_list(board, current_player)
222
+ coordinates_list = []
223
+
224
+ board.each do |row|
225
+ row.each do |loc|
226
+ if (loc != nil) and (loc.color == current_player)
227
+ jump_locations = jump_location_finder_stack(board, current_player, loc.x_pos, loc.y_pos)
228
+ coordinates_list << coordinates_of_computer_jump_landings(current_player, loc.x_pos, loc.y_pos, jump_locations)
229
+ end
230
+ end
231
+ end
232
+ coordinates_list.flatten
233
+ end
234
+
235
+ def generate_all_possible_moves(board, current_player)
236
+ all_moves = generate_normal_move_locations_list(board, current_player)
237
+ all_moves << generate_computer_jump_locations_list(board, current_player)
238
+ all_moves.flatten
239
+ end
240
+
241
+ def generate_computer_moves(board, current_player)
242
+ all_moves = generate_computer_jump_locations_list(board, current_player)
243
+ if all_moves.size == 0
244
+ all_moves << generate_normal_move_locations_list(board, current_player)
245
+ end
246
+ all_moves.flatten
247
+ end
248
+ end
@@ -0,0 +1,18 @@
1
+ class Checker
2
+ attr_accessor :x_pos, :y_pos, :color, :king_status
3
+
4
+ def initialize (x_location, y_location, color)
5
+ @x_pos = x_location
6
+ @y_pos = y_location
7
+ @color = color
8
+ @king_status = false
9
+ end
10
+
11
+ def is_king?
12
+ return @king_status
13
+ end
14
+
15
+ def make_king
16
+ @king_status = true
17
+ end
18
+ end
File without changes
@@ -0,0 +1,70 @@
1
+ class Evaluation
2
+
3
+ BOARD_WEIGHT = [ [ 4, nil, 4, nil, 4, nil, 4, nil],
4
+ [ nil, 3, nil, 3, nil, 3, nil, 4],
5
+ [ 4, nil, 2, nil, 2, nil, 3, nil],
6
+ [ nil, 3, nil, 1, nil, 2, nil, 4],
7
+ [ 4, nil, 2, nil, 1, nil, 3, nil],
8
+ [ nil, 3, nil, 2, nil, 2, nil, 4],
9
+ [ 4, nil, 3, nil, 3, nil, 3, nil],
10
+ [ nil, 4, nil, 4, nil, 4, nil, 4], ]
11
+
12
+ def evaluate_board(board)
13
+ player_value = 0
14
+ opponent_value = 0
15
+
16
+ #color == :red ? opponent_color = :black : opponent_color = :red
17
+
18
+ board.each do |row|
19
+ row.each do |position|
20
+ if position.nil? == false
21
+ position.color == :red ? player_value += calculate_value(position, :red) : opponent_value += calculate_value(position, :black)
22
+ end
23
+ end
24
+ end
25
+ #p "player value - opponent_value = #{player_value} - #{opponent_value}"
26
+ return player_value - opponent_value
27
+ end
28
+
29
+ def evaluate_board2(board)
30
+ red_count= 0
31
+ black_count = 0
32
+
33
+ board.each do |row|
34
+ row.each do |position|
35
+ if position.nil? == false
36
+ position.color == :red ? red_count += 1 : black_count += 1
37
+ end
38
+ end
39
+ end
40
+ #p "player value - opponent_value = #{player_value} - #{opponent_value}"
41
+ #p "IN EVALUATE 2: net count = > #{red_count - black_count}"
42
+ return red_count - black_count
43
+ end
44
+
45
+ def calculate_value(position, color)
46
+ if color == :red
47
+ if position.x_pos == 7
48
+ value = 10
49
+ elsif position.x_pos == 5 or position.x_pos == 6
50
+ value = 7
51
+ else
52
+ value = 5
53
+ end
54
+ elsif color == :black
55
+ if position.x_pos == 0
56
+ value = 10
57
+ elsif position.x_pos == 1 or position.x_pos == 2
58
+ value = 7
59
+ else
60
+ value = 5
61
+ end
62
+ end
63
+
64
+ if position.is_king?
65
+ value = 10
66
+ end
67
+ #p "IN CALCULATE VALUE : positon, color, value = (#{position.x_pos}, #{position.y_pos}) , #{color} , #{value}"
68
+ return value * BOARD_WEIGHT[position.x_pos][position.y_pos]
69
+ end
70
+ end
@@ -0,0 +1,68 @@
1
+ class Game
2
+
3
+ attr_accessor :board, :gui, :single_player_game, :current_player , :x_orig, :y_orig, :x_dest, :y_dest
4
+
5
+ def initialize(view)
6
+ @view = view
7
+ @current_player = :red
8
+ @bs = BoardSurvey.new
9
+ @evaluation = Evaluation.new
10
+ @input = UserInput.new
11
+ @move_check = MoveCheck.new
12
+ @board = Board.new
13
+ @game_board = @board.create_board
14
+ @minmax = Minimax.new(@bs, @evaluation)
15
+ end
16
+
17
+ def play_test_game
18
+ @game_board = @board.create_test_board
19
+ @board.add_checker(@game_board, :red, 0, 0)
20
+ @board.add_checker(@game_board, :red, 0, 2)
21
+ @board.add_checker(@game_board, :black, 7, 5)
22
+ @board.add_checker(@game_board, :black, 7, 7)
23
+ play_game
24
+ end
25
+
26
+ def play_game
27
+ @view.intro
28
+ player_input = @view.one_or_two_player
29
+ @single_player_game = true if player_input == "1\n"
30
+ while(game_over?(@game_board) == false)
31
+ message = nil
32
+ @view.render_board(@game_board)
33
+ if single_player_game and @current_player == :red
34
+ move = @minmax.best_move(@game_board, :red, 3)
35
+ coordinates = move
36
+ message = @move_check.move_validator(self, @game_board, :red, coordinates[0], coordinates[1], coordinates[2], coordinates[3])
37
+ else
38
+ coordinates = @view.get_move_coordinates(@current_player)
39
+
40
+ message = @move_check.move_validator(self, @game_board, @current_player, coordinates[0], coordinates[1], coordinates[2], coordinates[3])
41
+ end
42
+ puts message unless (message.nil? or message == "jumping move")
43
+ end
44
+ @view.render_board(@game_board)
45
+ @view.display_game_ending_message(@game_board)
46
+ end
47
+
48
+ def move(board, x_origin, y_origin, x_destination, y_destination)
49
+ #moving
50
+ moving_checker = board[x_origin][y_origin]
51
+
52
+ # set new location for checker
53
+ moving_checker.x_pos = x_destination
54
+ moving_checker.y_pos = y_destination
55
+
56
+ # update board positions of checker
57
+ board[x_origin][y_origin] = nil
58
+ board[x_destination][y_destination] = moving_checker
59
+ end
60
+
61
+ def game_over?(board)
62
+ @board.checkers_left(board, :black) == 0 or @board.checkers_left(board, :red) == 0
63
+ end
64
+
65
+ def self.switch_player(current_player)
66
+ current_player == :red ? :black : :red
67
+ end
68
+ end
@@ -0,0 +1,63 @@
1
+ class Gui
2
+
3
+ def initialize
4
+ @board = Board.new
5
+ @input = UserInput.new
6
+ end
7
+
8
+ def intro
9
+ puts 'Welcome to Checkers!'
10
+ end
11
+
12
+ def one_or_two_player
13
+ print "Do you want a one-player or two-player game (1 or 2) : "
14
+ response = gets
15
+ end
16
+
17
+ def get_move_coordinates(current_player)
18
+ print "#{current_player.to_s.upcase} make move(x1, y1, x2, y2): "
19
+ player_input = gets
20
+ coordinates = @input.translate_move_request_to_coordinates(player_input)
21
+ end
22
+
23
+ def render_board(board)
24
+ board_display = []
25
+ board_display << "\n 0 1 2 3 4 5 6 7 \n"
26
+ board_display << "\n -------------------------------------------------\n"
27
+
28
+ 0.upto(7) do |x_coord|
29
+ board_display << " #{x_coord} | "
30
+ 0.upto(7) do |y_coord|
31
+ if board[x_coord][y_coord].nil?
32
+ if (x_coord.even? && y_coord.odd?) || (x_coord.odd? && y_coord.even?)
33
+ board_display << "#" << " | "
34
+ elsif x_coord.even? && y_coord.even? || (x_coord.odd? && y_coord.odd?)
35
+ board_display << " " << " | "
36
+ end
37
+ elsif board[x_coord][y_coord].color == :red
38
+ if (board[x_coord][y_coord].is_king?) == true
39
+ board_display << "RK" << " | "
40
+ else
41
+ board_display << "R" << " | "
42
+ end
43
+ elsif board[x_coord][y_coord].color == :black
44
+ if (board[x_coord][y_coord].is_king?) == true
45
+ board_display << "BK" << " | "
46
+ else
47
+ board_display << "B" << " | "
48
+ end
49
+ end
50
+ end
51
+ board_display << "\n -------------------------------------------------\n"
52
+ end
53
+ board_display << "\n"
54
+ board_display.join
55
+ board_display.each { |line| print line}
56
+ end
57
+
58
+ def display_game_ending_message(board)
59
+ winner = @board.checkers_left(board, :black) == 0 ? :red : :black
60
+ winner = winner.to_s.capitalize
61
+ return "\n\nCongratulations, #{winner}, You have won!!!"
62
+ end
63
+ end
@@ -0,0 +1,151 @@
1
+ class Minimax
2
+
3
+ INFINITY = 100000
4
+
5
+ # Best move -> Have a method that generates all the possible moves, then apply the minimax to each one of those board positions, match the highest score
6
+ # and return the best move.
7
+ #
8
+ def initialize(board_survey, evaluation)
9
+ @bs = board_survey
10
+ @eval = evaluation
11
+ @jumped_checker = []
12
+ end
13
+
14
+ def best_move(board, player, depth)
15
+ #p "IN BEST MOVE : player -> #{player}"
16
+ move_scores = []
17
+ moves_list = @bs.generate_computer_moves(board, player)
18
+ moves_list.each_slice(4) do |move|
19
+ #p "IN BEST MOVE : before apply move"
20
+ apply_move(board, move)
21
+ move_scores << minimax(board, :black, depth)
22
+ #p "IN BEST MOVE : before unapply move"
23
+ unapply_move(board, move)
24
+ end
25
+ move_scores.flatten
26
+ #p "IN BEST MOVE : moves_list -> #{moves_list}"
27
+ #p "IN BEST MOVE : move_scores -> #{move_scores}"
28
+ best_score_index = move_scores.index(move_scores.max)
29
+ #p "IN BEST MOVE : best_score_index -> #{best_score_index}"
30
+ best_move_coords = moves_list.slice(best_score_index * 4, 4)
31
+ #p "IN BEST MOVE : best_move_coords -> #{best_move_coords}"
32
+ best_move_coords
33
+ end
34
+
35
+ def minimax(board, player, depth)
36
+ #p "IN MINIMAX: player -> #{player}"
37
+ if game_over?(board)
38
+ return who_won(board)
39
+ end
40
+
41
+ if depth == 0
42
+ evaluated_score = @eval.evaluate_board(board)
43
+ #p "IN MINIMAX -> returned value = #{evaluated_score}"
44
+ return @eval.evaluate_board(board)
45
+ else
46
+ player == :red ? best_score = -INFINITY : best_score = INFINITY
47
+
48
+ moves_list = @bs.generate_all_possible_moves(board, player)
49
+ #p "IN MINIMAX -> moves list = #{moves_list}"
50
+ if moves_list == nil
51
+ score = @eval.evaluate_board(board)
52
+ else
53
+ moves_list.each_slice(4) do |move|
54
+ #p "IN MINIMAX -> before apply move"
55
+ apply_move(board, move)
56
+ score = minimax(board, Game.switch_player(player), depth-1)
57
+ if player == :red
58
+ #p "IN MINIMAX -> in best score branch(red) -> #{best_score}"
59
+ best_score = score > best_score ? score : best_score
60
+ elsif player == :black
61
+ #p "IN MINIMAX -> in best score branch(black) -> #{best_score}"
62
+ best_score = score < best_score ? score : best_score
63
+ end
64
+ #p "IN MINIMAX -> before unapply move"
65
+ unapply_move(board, move)
66
+ #p "IN MINIMAX -> after unapply move board status = "
67
+ #puts Gui.render_board(board)
68
+ end
69
+ end
70
+ end
71
+ #p "IN MINIMAX -> best score = #{best_score}"
72
+ return best_score
73
+ end
74
+
75
+ def game_over?(board)
76
+ red_checkers = 0
77
+ black_checkers = 0
78
+
79
+ board.each do |row|
80
+ row.each do |position|
81
+ if position.nil? == false
82
+ position.color == :red ? red_checkers += 1 : black_checkers += 1
83
+ end
84
+ end
85
+ end
86
+ red_checkers == 0 or black_checkers == 0
87
+ end
88
+
89
+ def who_won(board)
90
+ red_checkers = 0
91
+ black_checkers = 0
92
+
93
+ board.each do |row|
94
+ row.each do |position|
95
+ if position.nil? == false
96
+ position.color == :red ? red_checkers += 1 : black_checkers += 1
97
+ end
98
+ end
99
+ end
100
+ red_checkers == 0 ? -INFINITY : INFINITY
101
+ end
102
+
103
+ def apply_move(board, move)
104
+ #p "IN APPLY MOVE: move -> #{move}"
105
+ checker = board[move[0]][move[1]]
106
+ board[move[2]][move[3]] = checker
107
+ board[move[0]][move[1]] = nil
108
+ checker.x_pos = move[2]
109
+ checker.y_pos = move[3]
110
+ if (move[2] - move[0]).abs == 2
111
+ #Board.remove_jumped_checker(board, move[0], move[1], move[2], move[3])
112
+ x_delta = (move[2] > move[0]) ? 1 : -1
113
+ y_delta = (move[3] > move[1]) ? 1 : -1
114
+ #p "MINIMAX::APPLY_MOVE -> move 1 - 4, x_delta, y_delta : #{move}, #{x_delta}, #{y_delta}"
115
+ #p "MINIMAX::APPLY_MOVE -> before assign, jumping_checker pos = #{@jumped_checker}"
116
+ @jumped_checker.push(board[move[0] + x_delta][move[1] + y_delta])
117
+ #p "MINIMAX::APPLY_MOVE -> jumped_checker = #{@jumped_checker}"
118
+ #p "MINIMAX::APPLY_MOVE -> jumped pos = #{move[0] + x_delta } , #{move[1] + y_delta }"
119
+ board[move[0] + x_delta][move[1] + y_delta] = nil
120
+ end
121
+ board
122
+ end
123
+
124
+ def unapply_move(board, move)
125
+ #p "IN UNAPPLY MOVE: move -> #{move}"
126
+ if (move[2] - move[0]).abs == 2
127
+ #Board.return_jumped_checker(board, move[0], move[1], move[2], move[3])
128
+ x_delta = (move[2] > move[0]) ? 1 : -1
129
+ y_delta = (move[3] > move[1]) ? 1 : -1
130
+
131
+ #p "MINIMAX::UNAPPLY_MOVE -> move 1 - 4, x_delta, y_delta : #{move}, #{x_delta}, #{y_delta}"
132
+ #p "MINIMAX::UNAPPLY_MOVE -> jumped_checker = #{@jumped_checker}"
133
+ #p "MINIMAX::UNAPPLY_MOVE -> jumped pos = #{move[0] + x_delta } , #{move[1] + y_delta }"
134
+ board[move[0] + x_delta][move[1] + y_delta] = @jumped_checker.pop
135
+ board
136
+ end
137
+ #p "IN UNAPPLY MOVE: before unapplied move, board at x-orig, y-orig -> #{board[move[0]][move[1]]} and x-dest, y-dest -> #{board[move[2]][move[3]]}"
138
+ checker = board[move[2]][move[3]]
139
+ board[move[0]][move[1]] = checker
140
+ board[move[2]][move[3]] = nil
141
+ checker.x_pos = move[0]
142
+ checker.y_pos = move[1]
143
+ #p "IN UNAPPLY MOVE: after unapplied move, board at x-orig, y-orig -> #{board[move[0]][move[1]]} and x-dest, y-dest -> #{board[move[2]][move[3]]}"
144
+ #p "IN UNAPPLY MOVE: after unapplied move, TOTAL BOARD IS : #{board}}"
145
+ board
146
+ end
147
+
148
+ def other_player(player)
149
+ player == :red ? :black : :red
150
+ end
151
+ end
@@ -0,0 +1,159 @@
1
+ class MoveCheck
2
+
3
+ attr_accessor :board, :current_player
4
+
5
+ def initialize
6
+ @survey = BoardSurvey.new
7
+ @consecutive_jumps = false
8
+ end
9
+
10
+ def move_validator(game, board, current_player, x_origin, y_origin, x_destination, y_destination)
11
+ message = nil
12
+
13
+ case
14
+ when (@consecutive_jumps and board[x_origin][y_origin] != @consecutive_jumper)
15
+ message = "You cannot jump with a different checker"
16
+ when out_of_bounds?(x_destination, y_destination)
17
+ message = "You cannot move off the board"
18
+
19
+ when no_checker_at_origin?(board, x_origin, y_origin)
20
+ message = "There is no checker to move in requested location"
21
+
22
+ when trying_to_move_opponents_checker?(board,current_player, x_origin, y_origin)
23
+ message = "You cannot move an opponents checker"
24
+
25
+ when trying_to_move_more_than_one_space_and_not_jumping?(x_origin, x_destination)
26
+ message = "You cannot move more than one space if not jumping"
27
+
28
+ when attempted_non_diagonal_move?(x_origin, y_origin, x_destination, y_destination)
29
+ message = "You can only move a checker diagonally"
30
+
31
+ when attempted_move_to_occupied_square?(board, x_destination, y_destination)
32
+ message = "You cannot move to an occupied square"
33
+
34
+ when non_king_moving_backwards?(board, current_player, x_origin, y_origin, x_destination)
35
+ message = "A non-king checker cannot move backwards"
36
+
37
+ when attempted_jump_of_empty_space?(board,current_player, x_origin, y_origin, x_destination, y_destination)
38
+ message = "You cannot jump an empty space"
39
+
40
+ when attempted_jump_of_own_checker?(board, current_player, x_origin, y_origin, x_destination, y_destination)
41
+ message = "You cannot jump a checker of your own color"
42
+
43
+ when jump_available_and_not_taken?(board, current_player, x_destination, y_destination)
44
+ message = "You must jump if a jump is available"
45
+
46
+ else
47
+ game.move(board, x_origin, y_origin, x_destination, y_destination)
48
+
49
+ if jumping_move?(x_origin, x_destination)
50
+ message = "jumping move"
51
+ Board.remove_jumped_checker(board, x_origin, y_origin, x_destination, y_destination)
52
+ @consecutive_jumps = false
53
+
54
+ if @survey.any_jumps_left?(board, current_player, x_destination, y_destination)
55
+ @consecutive_jumps = true
56
+ @consecutive_jumper = board[x_destination][y_destination]
57
+ end
58
+ end
59
+ Board.king_checkers_if_necessary(board)
60
+ end
61
+
62
+ if message == nil or (message == "jumping move" and @survey.any_jumps_left?(board, current_player, x_destination, y_destination)== false)
63
+ if @consecutive_jumps == false
64
+ game.current_player = Game.switch_player(game.current_player)
65
+ end
66
+ end
67
+ message
68
+ end
69
+
70
+ def jump_available?(board, current_player)
71
+ possible_jumps = @survey.generate_jump_locations_list(board, current_player)
72
+
73
+ possible_jumps.size > 0
74
+ end
75
+
76
+ def jump_available_and_not_taken?(board, current_player, x_destination, y_destination)
77
+ jump_possiblities = @survey.generate_jump_locations_list(board, current_player)
78
+
79
+ not_taken_jump = true
80
+ jump_possiblities.each_slice(2) do |i|
81
+ if(i[0] == x_destination) and (i[1] == y_destination)
82
+ not_taken_jump = false
83
+ end
84
+ end
85
+ (jump_available?(board, current_player) == true) and (not_taken_jump)
86
+ end
87
+
88
+ def jump_deltas(current_player, x_orig, y_orig, x_dest, y_dest)
89
+ deltas = []
90
+ x_dest > x_orig ? deltas << 1 : deltas << -1
91
+ y_dest > y_orig ? deltas << 1 : deltas << -1
92
+ end
93
+
94
+ def attempted_jump_of_empty_space?(board, current_player, x_origin, y_origin, x_destination, y_destination)
95
+ if jumping_move?(x_origin, x_destination)
96
+ deltas = jump_deltas(current_player, x_origin, y_origin, x_destination, y_destination)
97
+ board[x_origin + deltas[0]][y_origin + deltas[1]].nil? ? true : false
98
+ else
99
+ false
100
+ end
101
+ end
102
+
103
+ def attempted_jump_of_own_checker?(board, current_player, x_origin, y_origin, x_destination, y_destination)
104
+ if jumping_move?(x_origin, x_destination)
105
+ x_delta = (x_destination > x_origin) ? 1 : -1
106
+ y_delta = (y_destination > y_origin) ? 1 : -1
107
+
108
+ if current_player == :black
109
+ x_delta = (x_destination < x_origin) ? -1 : 1
110
+ y_delta = (y_destination < y_origin) ? -1 : 1
111
+ end
112
+
113
+ jumped_checker_x_value = x_origin + x_delta
114
+ jumped_checker_y_value = y_origin + y_delta
115
+
116
+ jumped_checker = board[jumped_checker_x_value][jumped_checker_y_value]
117
+
118
+ jumping_checker = board[x_origin][y_origin]
119
+
120
+ jumped_checker.color == jumping_checker.color
121
+ end
122
+ end
123
+
124
+ def jumping_move?(x_origin, x_destination)
125
+ (x_destination - x_origin).abs == 2
126
+ end
127
+
128
+ def out_of_bounds?(x, y)
129
+ ( x < 0 or y < 0) or (x > 7 or y > 7)
130
+ end
131
+
132
+ def no_checker_at_origin?(board, x_origin, y_origin)
133
+ board[x_origin][y_origin].nil?
134
+ end
135
+
136
+ def trying_to_move_opponents_checker?(board, current_player, x_origin, y_origin)
137
+ current_player != board[x_origin][y_origin].color
138
+ end
139
+
140
+ def trying_to_move_more_than_one_space_and_not_jumping?(x_origin, x_destination)
141
+ (x_destination - x_origin).abs > 2
142
+ end
143
+
144
+ def attempted_non_diagonal_move?(x_origin, y_origin, x_destination, y_destination)
145
+ (x_origin == x_destination) or (y_origin == y_destination)
146
+ end
147
+
148
+ def attempted_move_to_occupied_square?(board, x_destination, y_destination)
149
+ not board[x_destination][y_destination].nil?
150
+ end
151
+
152
+ def non_king_moving_backwards?(board, current_player, x_origin, y_origin, x_destination)
153
+ if current_player == :red
154
+ (x_destination < x_origin) and (board[x_origin][y_origin].is_king? == false)
155
+ else
156
+ (x_destination > x_origin) and (board[x_origin][y_origin].is_king? == false)
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,6 @@
1
+ class UserInput
2
+
3
+ def translate_move_request_to_coordinates(move_request)
4
+ coords_array = move_request.chomp.split(',').map { |x| x.to_i }
5
+ end
6
+ end
metadata CHANGED
@@ -1,45 +1,64 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: checkers_game_engine_sgallagher
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.2
3
+ version: !ruby/object:Gem::Version
5
4
  prerelease:
5
+ version: 0.0.3
6
6
  platform: ruby
7
- authors:
8
- - Steven Gallagher
7
+ authors:
8
+ - Steven Gallagher
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-26 00:00:00.000000000Z
12
+
13
+ date: 2012-01-26 00:00:00 Z
13
14
  dependencies: []
14
- description: Contains all the logic necessary to play a one or two player game of
15
- checkers
15
+
16
+ description: Contains all the logic necessary to play a one or two player game of checkers
16
17
  email: s.thomas.gallagher@gmail.com
17
18
  executables: []
19
+
18
20
  extensions: []
21
+
19
22
  extra_rdoc_files: []
20
- files: []
23
+
24
+ files:
25
+ - lib/draughts.rb
26
+ - lib/draughts/board.rb
27
+ - lib/draughts/board_survey.rb
28
+ - lib/draughts/checker.rb
29
+ - lib/draughts/computer_player.rb
30
+ - lib/draughts/evaluation.rb
31
+ - lib/draughts/game.rb
32
+ - lib/draughts/gui.rb
33
+ - lib/draughts/minimax.rb
34
+ - lib/draughts/move_check.rb
35
+ - lib/draughts/user_input.rb
21
36
  homepage: https://github.com/greenone1975/draughts
22
37
  licenses: []
38
+
23
39
  post_install_message:
24
40
  rdoc_options: []
25
- require_paths:
26
- - lib
27
- required_ruby_version: !ruby/object:Gem::Requirement
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
28
45
  none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
51
  none: false
35
- requirements:
36
- - - ! '>='
37
- - !ruby/object:Gem::Version
38
- version: '0'
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
39
56
  requirements: []
57
+
40
58
  rubyforge_project:
41
- rubygems_version: 1.8.15
59
+ rubygems_version: 1.8.9
42
60
  signing_key:
43
61
  specification_version: 3
44
62
  summary: Checkers game engine for one or two players
45
63
  test_files: []
64
+