python game of checkers

Asked

Viewed 1,308 times

2

i have an array of a checkers game state, how can I know which pieces are threatened on the board?

X5 is the threatened black parts and X6 is the threatened red parts, ie I want to know how to calculate the X5 and X6.

the code is as follows::

def estado_do_jogo_inicial():
    matriz= [[0, 1, 0, 1, 0, 1, 0, 1], 
             [1, 0, 1, 0, 1, 0, 1, 0],
             [0, 1, 0, 0, 0, 1, 0, 1],
             [0, 0, 0, 0, 11, 0, 0, 0],
             [0, 0, 0, 0, 0, 0, 0, 0],
             [2, 0, 2, 0, 2, 0, 2, 0],
             [0, 2, 0, 2, 0, 2, 0, 2],
             [2, 0, 2, 0, 2, 0, 2, 0]]

    return matriz

def caracteristicas_do_estado_de_jogo(estado_do_jogo):
    x1=x2=x3=x4=x5=x6=0

    for i in range (len(estado_do_jogo)):
        for j in range(len(estado_do_jogo[i])):
            if estado_do_jogo[i][j] == 1:
                x1 += 1 
            elif estado_do_jogo[i][j] == 2:
                x2 += 1
            elif estado_do_jogo[i][j] == 11:
                x3 += 1
            elif estado_do_jogo[i][j] == 22:
                x4 += 1

    return x1, x2, x3, x4, x5, x6

    x1, x2, x3, x4, x5, x6 = caracteristicas_do_estado_de_jogo(e1)

    print(f'a quantidade de peças pretas no tabuleiro (x1) é igual à: ', x1)
    print(f'a quantidade de peças vermelhas no tabuleiro (x2) é igual à: ', x2)
    print(f'a quantidade de damas pretas no tabueleiro (x3) é igual à: ', x3)
    print(f'a quantidade de damas vermelhas no tabuleiro (x4) é igual à: ', x4)
  • 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?

  • @Leticia - as my answer involves more advanced language features than you were using, any doubt can write. (another question, or comment here)

1 answer

4


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:

  • for each board position,:

    • if there is a piece of the desired team in position:
    • for each diagonal direction:
      • if the opposite house exists and is free and\
      • if following the diagonal there is a normal piece or an opponent’s lady:
        • then add 1 to threat count

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
  • "As you are taking into account the ladies, you have to look back at all the houses on the same diagonal" In Brazil we really play this way, but there are many countries where the lady can only walk 1 house (the difference between the lady and the normal piece, in this case, is that the lady can walk backwards too). I believe that the OP is based on the Brazilian rules, but the ideal was the OP explain this detail, which is crucial for the algorithm.

  • I really look at the four directions - but the "back" in that sentence is in relation to the distance - "behind the piece that’s being taken into consideration" - in that sentence, in any of the four directions. The board does not implement the full rules of the game - to do this, as you say, it is necessary to know which ones rules, and preferably leave configurable.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.