-
Notifications
You must be signed in to change notification settings - Fork 0
/
board.rb
183 lines (155 loc) · 4.27 KB
/
board.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
require_relative 'node'
# Class that represents a board of the game
class Board
# -- Acessors
attr_accessor :size
attr_reader :current_state
attr_reader :root
attr_reader :final_state
# Initializer
def initialize(size, initial_state = nil, final_state = nil)
@size = size
initialize_initial_and_final_state(initial_state, final_state)
@root = Node.new(@current_state)
end
# Pretty Print
def pretty_print
puts '--- Board ---'
pretty_print_line_divider
@current_state.each do |row|
pretty_print_row(row)
pretty_print_line_divider
end
end
# Given a state returns true or false based on wheter it is the desired
# solution
def solution?(state)
state.current_state == final_state.current_state
end
# Given a board, check if it has a valid solution
def solvable(board)
# Gets parity and blank position
parity, blank = get_parity_and_blank(board.flatten)
# Checks based on parity and blank position if it is solvable
check_parity_and_blank(parity, blank)
end
private
def initialize_initial_and_final_state(initial, final)
initialize_initial_state(initial)
initialize_final_state(final)
end
def initialize_initial_state(state)
@current_state = if state.nil? || state.length != size
random_board
else
state
end
end
def initialize_final_state(state)
@final_state = if state.nil?
Node.new(
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 0]
]
)
else
Node.new(state)
end
end
# ---------- Board Auxiliary Methods
# Generaters a new valid board
def new_board
random_board
end
# Generates a new random board
# TODO: check assigned branch condition of this method
def random_board
# Loop generating random boards until one solvable comes up
loop do
# Initialize array with sequential values and then shuffles
temp = (0..(size * size - 1)).to_a.shuffle
# Put shuffled values in two dimensional square matrix and returns
board = Array.new(@size) do |i|
Array.new(size) { |j| temp[i * size + j] }
end
# break loop and returns board if it is solvable
return board if solvable(board)
end
end
# ----------
# Calculates parity and blank position for a flattened (1 dimension) board
def get_parity_and_blank(flattened_board)
# Current row
row = 0
# Parity
parity = 0
# Row with the blank space
blank = 0
(0..(size - 1)).each do |i|
# Advance to next row if
row += 1 if i % size
if flattened_board[i].zero?
blank = row
next
end
# Updates parity
parity = update_parity(flattened_board, parity, i)
end
# Return parity and blank
[parity, blank]
end
# Updates parity of a flattened board given a certain position
def update_parity(flattened_board, parity, current_pos)
# Loops through next positions, updating parity
((current_pos + 1)..(flattened_board.length - 1)).each do |j|
if flattened_board[current_pos] > flattened_board[j] &&
flattened_board[j] != 0
parity += 1
end
end
# Returns updated parity
parity
end
# Checks based on board size, parity and blank position
# if the board is solvable
def check_parity_and_blank(parity, blank)
# if size.even?
# if blank.even?
# parity.even?
# else
# parity.odd?
# end
# else
# parity.even?
# end
if size.odd?
parity.even?
elsif parity.even? && blank.odd?
true
elsif parity.odd? && blank.even?
true
else
false
end
end
# ------------Pretty Print Auxiliary Methods
# Auxiliary function to print a row of the board separated by '|'
def pretty_print_row(row)
row.each do |e|
if e.zero?
printf '| '
else
printf "|#{e}"
end
end
printf "|\n"
end
# Auxiliary function to print a line divider
def pretty_print_line_divider
size.times { printf '--' }
printf "-\n"
end
# ----------
end