/
tic_tac_toe.rb
154 lines (127 loc) · 3.07 KB
/
tic_tac_toe.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
class Array
def all_empty?
self.all? { |element| element.to_s.empty? }
end
def all_same?
self.all? { |element| element == self[0] }
end
def any_empty?
self.any? { |element| element.to_s.empty? }
end
def none_empty?
!any_empty?
end
end
module TicTacToe
class Game
attr_reader :players, :board, :current_player, :other
def initialize(players, board = Board.new)
@players = players
@board = board
@current_player, @other_player = players.shuffle
end
def switch_players
@current_player, @other_player = @other_player, @current_player
end
def solicit_move
"#{current_player.name}: Enter a number between 1 and 9 to make your move."
end
def get_move(human_move = gets.chomp)
human_move_to_coordinate(human_move)
end
def game_over_message
return "#{current_player.name} won!" if board.game_over == :winner
return "The game ended in a tie" if board.game_over == :draw
end
def play
puts "#{current_player.name} has randomly been selected as the first player"
while true
board.formatted_grid
puts ""
puts solicit_move
x, y = get_move
board.set_cell(x, y, current_player.color)
if board.game_over
puts game_over_message
board.formatted_grid
return
else
switch_players
end
end
end
private
def human_move_to_coordinate(human_move)
mapping = {
"1" => [0,0],
"2" => [1,0],
"3" => [2,0],
"4" => [0,1],
"5" => [1,1],
"6" => [2,1],
"7" => [0,2],
"8" => [1,2],
"9" => [2,2]
}
mapping[human_move]
end
end
class Board
attr_accessor :grid
def initialize(input = {})
@grid = input.fetch(:grid, default_grid)
end
def draw?
grid.flatten.map { |cell| cell.value }.none_empty?
end
def winner?
winning_positions.each do |winning_position|
next if winning_position_values(winning_position).all_empty?
return true if winning_position_values(winning_position).all_same?
end
false
end
def game_over
return :winner if winner?
return :draw if draw?
false
end
private
def default_grid
Array.new(3) { Array.new(3) { Cell.new } }
end
def get_cell(x, y)
grid[x][y]
end
def set_cell(x, y, value)
get_cell(x, y).value = value
end
def winning_positions
grid +
grid.transpose +
diagonals
end
def diaganols
[
[get_cell(0,0), get_cell(1,1), get_cell(2,2)],
[get_cell(0,2), get_cell(1,1), get_cell(2,0)]
]
end
def winning_position_values(winning_position)
winning_position.map { |cell| cell.value }
end
end
class Cell
attr_accessor :value
def initialize(value="")
@value = value
end
end
class Player
attr_accessor :color, :name
def initialize(input)
@color = input.fetch(:color)
@name = input.fetch(:name)
end
end
end