see_states = True
class block:
def __init__(self, x, y, col, h=1, w=1, _type="normal"):
self.x, self.y = x, y
self.col = col
self.h, self.w = h, w
self.type = _type
def __repr__(self):
return f"x: {self.x}, y: {self.y}, height: {self.h}, width: {self.w}, color: {self.col}"
class power_gem(block):
def __init__(self, x, y, col, h=2, w=2):
assert h >= 2 and w >= 2, "Powergem cannot be formed"
super().__init__(x, y, col.upper(), h, w)
def __repr__(self):
return super().__repr__()
class block_pair:
def __init__(self, fst, snd):
self.fst = fst
self.snd = snd
def __repr__(self):
return f"first : {self.fst}\nsecond: {self.snd}"
def make_move(self, t):
assert t in "ABLR", "Invalid move!"
if t in "LR":
self.move_hrz(t)
elif t in "AB":
self.rotate(t)
def move_hrz(self, t):
if t == "L" and min(self.fst.y, self.snd.y) > 0:
self.fst.y -= 1
self.snd.y -= 1
elif t == "R" and max(self.fst.y, self.snd.y) < 5:
self.fst.y += 1
self.snd.y += 1
def rotate(self, t):
dx, dy = self.snd.x - self.fst.x, self.snd.y - self.fst.y
if t == "A":
self.snd.x = self.fst.x - dy
self.snd.y = self.fst.y + dx
else:
self.snd.x = self.fst.x + dy
self.snd.y = self.fst.y - dx
if max(self.fst.y, self.snd.y) == 6:
self.fst.y -= 1
self.snd.y -= 1
elif min(self.fst.y, self.snd.y) == -1:
self.fst.y += 1
self.snd.y += 1
class game:
def __init__(self):
self.board = [[" " for _ in range(6)] for _ in range(12)]
self.clusters = []
self.current_pair = None
self.crash_gems = []
self.power_gems = []
self.rainbow_gems = []
self.states = []
def __repr__(self):
r = [" ___ __ "]
for i, row in enumerate(self.board):
r.append(str(i).rjust(2, ' ') + '|' + "".join(row) +'|')
r.append(" " + "*" * 6)
return "\n".join(r) + "\n"
# used for real test
def __str__(self):
return "\n".join("".join(x) for x in self.board)
def move_current_pair(self, moves):
for move in moves:
assert move in "LRAB", "Impossible move!"
self.current_pair.make_move(move)
def make_block(col, pos):
assert pos in (0, 1), "Can't make block from pair"
#initial height is negative before movement of pair can start
return block(-2 + pos, 3, col)
def fall_current_pair(self):
#just in case...
while min(self.current_pair.fst.x, self.current_pair.snd.x) < 0:
self.current_pair.fst.x += 1
self.current_pair.snd.x += 1
x1, y1 = self.current_pair.fst.x, self.current_pair.fst.y
x2, y2 = self.current_pair.snd.x, self.current_pair.snd.y
if self.board[x1][y1] != ' ' or self.board[x2][y2] != ' ':
return False
while True:
yf, ys = self.current_pair.fst.y, self.current_pair.snd.y
assert 0 <= ys < 6 and 0 <= yf < 6, "Wrong position for current pair"
m = max(self.current_pair.fst.x, self.current_pair.snd.x)
#Stops the fall if the block hits the ground or a another block in the next step
if m == 11 or (m + 1 < 12 and (self.board[m + 1][yf] != " " or self.board[m + 1][ys] != " ")):
break
#otherwise continue falling
self.current_pair.fst.x += 1
self.current_pair.snd.x += 1
#the pair is blocked, the first block f is the lowest
if self.current_pair.fst.x >= self.current_pair.snd.x:
f, s = self.current_pair.fst, self.current_pair.snd
else:
f, s = self.current_pair.snd, self.current_pair.fst
#the pair is separated...
#move the first block independantly
while f.x + 1 < 12 and self.board[f.x + 1][f.y] == " ":
f.x += 1
#update the board...
self.board[f.x][f.y] = f.col
#move the second block independantly
while s.x + 1 < 12 and self.board[s.x + 1][s.y] == " ":
s.x += 1
#update the board...
self.board[s.x][s.y] = s.col
# print("after pair falls :\n", self.__repr__())
return True
def get_adj(self, i, j, col):
# Getting pos of all adjacents color blocks (simple, crashgems and powergems)
queue = [(i, j)]
visited = set([(i, j)])
power_gem_visited = set()
while len(queue) > 0:
x, y = queue.pop(0)
for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
u, v = x + dx, y + dy
if (0 <= u < 12 and 0 <= v < 6 and self.board[u][v].upper() == col.upper() and not (u, v) in visited):
queue.append((u, v))
visited.add((u, v))
rem = set()
for gem in self.power_gems:
x, y, c, h, w = gem.x, gem.y, gem.col, gem.h, gem.w
if (x, y) in visited:
power_gem_visited.add((x, y, c, h, w))
for u in range(x, x + h):
for v in range(y, y + w):
rem.add((u, v, c, h, w))
visited -= set([(u, v) for (u, v, c, h, w) in rem])
return (visited, power_gem_visited)
def has_crash_gems(self):
return any(c in "".join("".join(x) for x in self.board) for c in "rgyb")
# crash gems lower case!!
def get_crash_gems(self):
gems = set()
for x in range(11, -1, -1):
for y in range(6):
c = self.board[x][y]
if c in "rgyb":
gems.add((x, y, c))
return gems
# rainbow gems lower case!!
def get_rainbow_gems(self):
gems = set()
for x in range(11, -1, -1):
for y in range(6):
if self.board[x][y] == '0':
gems.add((x, y))
return gems
# method for crash gems
def destroy_by_crash_gems(self):
for (x, y, c) in self.get_crash_gems():
adj, pow_adj = self.get_adj(x, y, c)
print("crashed :", c, adj, pow_adj)
if len(adj) + len(pow_adj) > 1:
self.board[x][y] = ' '
if len(adj) > 1:
for u, v in adj:
self.board[u][v] = ' '
if len(pow_adj) > 0:
idxs = []
for i, g in enumerate(self.power_gems):
_x, _y, _c, _h, _w = g.x, g.y, g.col, g.h, g.w
if (_x, _y, _c, _h, _w) in pow_adj:
idxs.append(i)
# updating board
for u in range(_x, _x + _h):
for v in range(_y, _y + _w):
self.board[u][v] = ' '
# removing power gems
self.power_gems = [self.power_gems[i] for i in range(len(self.power_gems)) if not i in idxs]
print("after destroy procedure :\n", self.__repr__())
return
# method for rainbow gems
def destroy_by_rainbow_gems(self):
for x, y in self.get_rainbow_gems():
self.board[x][y] = ' '
if x != 11:
# getting color of bottom block if it exists
col = self.board[x + 1][y].upper()
# deleting power gems
for u in range(12):
for v in range(6):
if self.board[u][v].upper() == col:
self.board[u][v] = ' '
# removing power gems
self.power_gems = [gem for gem in self.power_gems if gem.col != col]
def get_simple_color_gems(self):
rem = set()
for gem in self.power_gems:
x, y, c, h, w = gem.x, gem.y, gem.col, gem.h, gem.w
for u in range(x, x + h):
for v in range(y, y + w):
rem.add((u, v))
gems = set()
for x in range(12):
for y in range(6):
# only add simple blocks not part of power gems
if self.board[x][y].upper() in "RGYB" and not (x, y) in rem:
gems.add((x, y, self.board[x][y]))
return gems
def make_power_gems(self):
candidates = sorted(self.get_simple_color_gems(), key=lambda x:(x[0], x[1]))
new_power_gems = []
for x, y, col in candidates:
# square 2 * 2 of simple blocks of same color
if x + 1 < 12 and y + 1 < 6 and all((u, v, col) in candidates for u, v in ((x + 1, y), (x + 1, y + 1), (x, y + 1))):
# expand horizontally
w = 2
while y + w < 6 and (x, y + w, col) in candidates and (x + 1, y + w, col) in candidates:
w += 1
# expand vertically
h = 2
while all((x + h, v, col) in candidates for v in range(y, y + w)):
h += 1
self.power_gems.append(power_gem(x, y, col, h, w))
print("new power gem from simple", self.power_gems[-1])
return True
return False
def fall_power_gems(self):
idx = None
self.power_gems.sort(key=lambda g: (-g.x, g.y))
for i, gem in enumerate(self.power_gems):
x, y, c, h, w = gem.x, gem.y, gem.col, gem.h, gem.w
if x + h < 12 and all(self.board[x + h][v] == ' ' for v in range(y, y + w)):
k = 0
while x + h + k < 12 and all(self.board[x + h + k][v] == ' ' for v in range(y, y + w)):
k += 1
idx = i
if k > 0:
gem = self.power_gems[idx]
print("falling power gem", gem, k)
x, y, c, h, w = gem.x, gem.y, gem.col, gem.h, gem.w
self.power_gems = [gem for i, gem in enumerate(self.power_gems) if i != idx]
for u in range(x, x + h):
for v in range(y, y + w):
self.board[u][v] = ' '
self.power_gems.append(power_gem(x + k, y, c, h, w))
for u in range(x + k, x + k + h):
for v in range(y, y + w):
self.board[u][v] = c
return None
def fall_simple_gems(self):
for x, y, c in sorted(self.get_simple_color_gems(), key=lambda e: (-e[0], e[1])):
if x + 1 < 12 and self.board[x + 1][y] == ' ':
k = 0
while x + k + 1 < 12 and self.board[x + k + 1][y] == ' ':
k += 1
self.board[x][y], self.board[x + k][y] = self.board[x + k][y], self.board[x][y]
return None
def fall(self):
while 1:
st = str(self)
self.fall_simple_gems()
self.fall_power_gems()
if str(self) == st:
break
def grow_power_gems_hrz(self):
# grow with another power gem
power_gems_sorted = sorted(self.power_gems, key=lambda e: (e.x, e.y))
N = len(power_gems_sorted)
ii, jj, col = None, None, None
for i in range(N):
for j in range(i + 1, N):
gi, gj = power_gems_sorted[i], power_gems_sorted[j]
xi, yi, ci, hi, wi = gi.x, gi.y, gi.col, gi.h, gi.w
xj, yj, cj, hj, wj = gj.x, gj.y, gj.col, gj.h, gj.w
if yj == yi + wi and xi == xj and ci == cj and hi == hj:
self.power_gems = [gem for l, gem in enumerate(power_gems_sorted) if l != i and l != j]
self.power_gems.append(power_gem(gi.x, gi.y, gi.col, gi.h, gi.w + gj.w ))
print("p -> p (hrz)", gi, gj)
return True
# grow with simple power gems
simple_gems = self.get_simple_color_gems()
print("simple right", simple_gems)
for i in range(N):
gi = power_gems_sorted[i]
x, y, c, h, w = gi.x, gi.y, gi.col, gi.h, gi.w
# growing to the right
k = 0
while all((u, y + w + k, c) in simple_gems for u in range(x, x + h)):
k += 1
if k > 0:
print("p -> n (right)", gi, k)
self.power_gems = [gem for u, gem in enumerate(power_gems_sorted) if u != i]
self.power_gems.append(power_gem(x, y, c, h, w + k))
return True
# growing to the left
l = 0
while all((u, y - (l + 1), c) in simple_gems for u in range(x, x + h)):
l += 1
if l > 0:
print("p -> n (left)", gi, l)
self.power_gems = [gem for u, gem in enumerate(power_gems_sorted) if u != i]
self.power_gems.append(power_gem(x, y - l, c, h, w + l))
return True
return False
def grow_power_gems_vrt(self):
# grow with another power gem
power_gems_sorted = sorted(self.power_gems, key=lambda e: (e.x, e.y))
N = len(power_gems_sorted)
for i in range(N):
for j in range(i + 1, N):
gi, gj = power_gems_sorted[i], power_gems_sorted[j]
xi, yi, ci, hi, wi = gi.x, gi.y, gi.col, gi.h, gi.w
xj, yj, cj, hj, wj = gj.x, gj.y, gj.col, gj.h, gj.w
if xj == xi + hi and yi == yj and ci == cj and wi == wj:
self.power_gems = [gem for l, gem in enumerate(power_gems_sorted) if l != i and l != j]
self.power_gems.append(power_gem(gi.x, gi.y, gi.col, gi.h + gj.h, gi.w))
print("p -> p (vrt)", gi, gj)
return True
# grow with simple power gems
simple_gems = self.get_simple_color_gems()
for i in range(N):
gi = power_gems_sorted[i]
x, y, c, h, w = gi.x, gi.y, gi.col, gi.h, gi.w
# growing down
k = 0
while all((x + h + k, v, c) in simple_gems for v in range(y, y + w)):
k += 1
if k > 0:
print("p -> n (down)", gi, k)
self.power_gems = [gem for u, gem in enumerate(power_gems_sorted) if u != i]
self.power_gems.append(power_gem(x, y, c, h + k, w))
return True
# growing up
l = 0
while all((x - (l + 1), v, c) in simple_gems for v in range(y, y + w)):
l += 1
if l > 0:
self.power_gems = [gem for u, gem in enumerate(power_gems_sorted) if u != i]
self.power_gems.append(power_gem(x - l, y, c, h + l, w))
print("p -> n (up)", gi, l)
return True
return False
def play(self, instructions):
while len(instructions) > 0:
print("power gems :")
print('\n'.join(str(g) for g in self.power_gems))
pair, moves = instructions.pop(0)
print("pair :", pair, "\nmove :", moves ,'\n')
self.current_pair = block_pair(game.make_block(pair[0], 0), game.make_block(pair[1], 1))
self.move_current_pair(moves)
b = self.fall_current_pair()
if not b:
break
self.destroy_by_rainbow_gems()
while 1:
st = str(self)
self.fall()
self.destroy_by_crash_gems()
b1 = self.make_power_gems()
b2 = self.grow_power_gems_hrz()
b3 = self.grow_power_gems_vrt()
print(b1, b2, b3)
if str(self) == st and not b1 and not b2 and not b3:
break
print(self.__repr__())
# self.states.append("\n".join("".join(x) for x in self.board))
def puzzle_fighter(arr):
g = game()
g.play(arr[:])
return str(g)
arr = [['rB', 'B'], ['YG', 'RR'], ['bG', 'BBB'], ['GG', 'ARR'], ['GB', ''], ['BY', 'BBBL'], ['Ry', 'RR'], ['YB', 'RR'], ['GY', 'RRR'], ['GB', 'B'], ['YY', 'A'], ['YR', 'ALLL'], ['Br', 'AAALL'], ['yr', 'AALL'], ['Y0', 'ARR'], ['GB', 'AAALLL'], ['YB', 'AAARRR'], ['BR', 'BRR'], ['GY', 'AAAR'], ['BB', 'BBBR'], ['GR', 'R'], ['GG', 'AA'], ['YB', 'AAAL'], ['RY', 'ALLL'], ['YB', 'AARR'], ['YB', 'BR']]
res = puzzle_fighter(arr)
To embed this program on your website, copy the following code and paste it into your website's HTML: