To know about threatened pieces, it has to be a check that looks, for each piece, all the diagonals in which it is, and if there are any "back" pieces. As you are taking into account the checkers, have to look all the houses back on the same diagonal - don’t just put some "if" and look at the adjacent houses.
So, the recomordination there is to structure a little better the representation of the board, instead of simply "lists within lists" - create a class that allows you to type a little to know what is in each house - in Python, a class with the method __getitem__
, for example, allows the use of direct board coordinates in square brackets. And then you can put various little methods to check diagonals and positions, and compose their use. Methods to move pieces that check the legality of a move, and so on.
Taking the opportunity, it is also worth creating a simple class to represent the game pieces, instead of using arbitrary values like 1, 11, 2 and 22 - which can allow "smart" comparisons that only return "True"for example, if the piece on the other side is the opponent without having to duplicate all the logic of the comparisons.
In short - there is no shortcut - The more you can factor the code into small functions and abstract things like, use relative positions, the more readable the final code becomes.
I created a board class here - to facilitate includes a function that can popular the board from the list of lists as you created -
I use coordinates from the lower left corner, from 0 to 7 and 0 to 7, or the notation for coordinates "A1, B1, up to H8", used in chess boards - so to the popular I invert the coordinate y.
(it’s also great to be able to visualize what’s on the board - so it includes a representation for the black tiles and squares using Unicode characters - I searched for "Circle" here: https://www.fileformat.info/info/unicode/char/search.htm?q=circle&preview=entity ) )
As for the algorithm itself to locate the threatened pieces, this is how we would describe the task in English:
class PlayingPiece:
def __init__(self, type, team):
self.type = type
self.team = team
def __eq__(self, other):
return self.team == other.team
def __str__(self):
# ◯ , ⏺, ②, ❷
return (
"\N{LARGE CIRCLE}" if self.type == "peça" and self.team == "vermelha" else
"\N{BLACK CIRCLE FOR RECORD}" if self.type == "peça" and self.team == "preta" else
"\N{CIRCLED DIGIT TWO}" if self.type == "dama" and self.team == "vermelha" else
"\N{DINGBAT NEGATIVE CIRCLED DIGIT TWO}"
)
def __repr__(self):
return f"{self.type} {self.team}"
teams = ["preta", "vermelha"]
types_ = ["peça", "dama"]
class Board:
def __init__(self, size=(8,8)):
self.size = size
self.data = [None,] * (size[0] * size[1])
self.player = teams[0]
def load_from_legacy_lists(self, lists):
for y, row in enumerate(lists):
y = self.size[0] - 1 - y
for x, p in enumerate(row):
piece = (None if p == 0 else
PlayingPiece("peça", "preta") if p == 1 else
PlayingPiece("peça", "vermelha") if p == 2 else
PlayingPiece("dama", "preta") if p == 11 else
PlayingPiece("dama", "vermelha")
)
if piece:
self[y, x] = piece
def filter_pos(self, pos):
if 0 <= pos[0] < self.size[0] and 0 <= pos[1] < self.size[1]:
return True
return False
def black_pos(self, pos):
return not (pos[0] + pos[1] % 2) % 2
def _norm_pos(self, pos):
if len(pos) != 2:
raise ValueError()
if isinstance(pos, str):
pos = (ord(pos[0].lower()) - ord("a"), int(pos[1]) - 1)
if not self.black_pos(pos):
raise ValueError("Coordenada não está nas casas pretas")
if not self.filter_pos(pos):
raise ValueError("Coordenada inválida")
return pos
def __getitem__(self, pos):
pos = self._norm_pos(pos)
return self.data[self.size[0] * pos[0] + pos[1]]
def __setitem__(self, pos, value):
pos = self._norm_pos(pos)
if value is not None and not isinstance(value, PlayingPiece):
raise TypeError("Apenas peças de jogo ou None são aceitos")
self.data[self.size[0] * pos[0] + pos[1]] = value
def iter_directions(self):
for y in -1, 1:
for x in -1, 1:
yield y, x
def _check_menace(self, pos, direction):
other_dir = -direction[0], -direction[1]
oposite_square = pos[0] + other_dir[0], pos[1] + other_dir[1]
if not self.filter_pos(oposite_square) or self[oposite_square]:
# não há casa oposta, ou está ocupada
return False
for i in range(1, 1 + self.size[0]):
square = pos[0] + i * direction[0], pos[1] + i * direction[1]
if not self.filter_pos(square):
# fora do tabuleiro
return False
item = self[square]
if item and item.team != self.player and (
i == 1 or item.type == "dama"
):
return True
def __iter__(self):
"""yield all valid board positions"""
for y in range(self.size[0]):
for x in range(self.size[1]):
try:
self._norm_pos((x,y))
except ValueError:
continue
yield (y, x)
def count_menace(self, pos):
pos = self._norm_pos(pos)
piece = self[pos]
last_direction = None
menace_count = sum(int(self._check_menace(pos, direction)) for direction in self.iter_directions())
return menace_count
def count_team_menace(self, team):
self.team = team
menaces = 0
for pos in self:
if self[pos] and self[pos].team == team:
menaces += self.count_menace(pos)
return menaces
def __repr__(self):
lines = []
for y in range(self.size[0] - 1, -1, -1):
line = ""
for x in range(self.size[1]):
if not self.black_pos((y, x)):
line += " "
continue
item = self[y, x]
line += ("\u2588" if not item else
str(item)
)
lines.append(line)
return "\n".join(lines)
Using this in interactive mode:
In [120]: aa = Board()
...: aa[0,0] = PlayingPiece("peça", "vermelha")
...: aa[1, 1] = PlayingPiece("peça", "preta")
...:
...:
In [121]: aa
Out[121]:
█ █ █ █
█ █ █ █
█ █ █ █
█ █ █ █
█ █ █ █
█ █ █ █
⏺ █ █ █
◯ █ █ █
In [122]: aa.count_team_menace("preta")
Out[122]: 1
The best is to scroll through the array and check if the neighbors have an empty field, or if they have any pieces of your or the opponent’s. What have you tried? There’s some code to your attempts?
– lazyFox
@Leticia - as my answer involves more advanced language features than you were using, any doubt can write. (another question, or comment here)
– jsbueno