Search code examples
pythonpython-3.xmatrixchess

Logic used to move in a matrix isn't working


I'm building chess in python. I'm struggling to move a piece on the board, which is comprised of 8 lists like so:

[['___' for z in range(x)] for z in range(x)]  # z is 8 in this instance

Pretty printed it looks like this:

      a      b      c      d      e      f      g      h

8  ['___', '___', '___', '___', '___', '___', '___', '___']  8

7  ['___', '___', '___', '___', '___', '___', '___', '___']  7

6  ['___', '___', '___', '___', '___', '___', '___', '___']  6

5  ['___', '___', '___', '___', '___', '___', '___', '___']  5

4  ['___', '___', '___', '___', '___', '___', '___', '___']  4

3  ['___', '___', '___', '___', '___', '___', '___', '___']  3

2  ['___', '___', '___', '___', '___', '___', '___', '___']  2

1  ['___', '___', '___', '___', '___', '___', '___', '___']  1

      a      b      c      d      e      f      g      h

I place a piece on the board:

def create(self):
        Config.board[self.y][self.x] = self.pieceid

Then it looks like this:

      a      b      c      d      e      f      g      h

8  ['___', '___', '___', '___', '___', '___', '___', '___']  8

7  ['___', '___', '___', '___', '___', '___', '___', '___']  7

6  ['___', '___', '___', '___', '___', '___', '___', '___']  6

5  ['___', '___', '___', '___', '___', '___', '___', '___']  5

4  ['___', '___', '___', '___', '___', '___', '___', '___']  4

3  ['___', '___', '___', '___', '___', '___', '___', '___']  3

2  ['___', '___', '___', '___', '___', '___', '___', '___']  2

1  ['___', '___', '___', '___', 'wN1', '___', '___', '___']  1

      a      b      c      d      e      f      g      h

Now I check for the possible moves of the Knight (being checked isn't implemented yet):

def possible_moves(self):
        pos_moves = []

        # Up, Right (1 space, 2 spaces)
        try:
            if 1 <= self.x + 2 <= len(Config.board) and 1 <= self.y - 1 <= len(Config.board):
                if Config.board[self.x + 2][self.y - 1] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x + 2)}{Config.tile_convert(self.y - 1, True)}')
        except IndexError: pass

        #Up, Left
        try:
            if 1 <= self.x - 2 <= len(Config.board) and 1 <= self.y - 1 <= len(Config.board):
                if Config.board[self.x - 2][self.y - 1] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x - 2)}{Config.tile_convert(self.y - 1, True)}')
        except IndexError: pass

        # Down, Right
        try:
            if 1 <= self.x + 2 <= len(Config.board) and 1 <= self.y + 1 <= len(Config.board):
                if Config.board[self.x + 2][self.y + 1] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x + 2)}{Config.tile_convert(self.y + 1, True)}')
        except IndexError: pass

        #Down, Left
        try:
            if 1 <= self.x - 2 <= len(Config.board) and 1 <= self.y + 1 <= len(Config.board):
                if Config.board[self.x - 2][self.y + 1] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x - 2)}{Config.tile_convert(self.y + 1, True)}')
        except IndexError: pass

        # Right, Up
        try:
            if 1 <= self.x + 1 <= len(Config.board) and 1 <= self.y - 2 <= len(Config.board):
                if Config.board[self.x + 1][self.y - 2] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x + 1)}{Config.tile_convert(self.y - 2, True)}')
        except IndexError: pass

        # Right, Down
        try:
            if 1 <= self.x + 1 <= len(Config.board) and 1 <= self.y + 2 <= len(Config.board):
                if Config.board[self.x + 1][self.y + 2] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x + 1)}{Config.tile_convert(self.y + 2, True)}')
        except IndexError: pass

        #Left, Up
        try:
            if 1 <= self.x - 1 <= len(Config.board) and 1 <= self.y - 2 <= len(Config.board):
                print('Current: ', self.x, self.y)
                print('New: ', self.x - 1, self.y - 2)
                if Config.board[self.x - 1][self.y - 2] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x - 1)}{Config.tile_convert(self.y - 2, True)}')
        except IndexError: pass

        # Left, Down
        try:
            if 1 <= self.x - 1 <= len(Config.board) and 1 <= self.y + 2 <= len(Config.board):
                if Config.board[self.x - 1][self.y + 2] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x - 1)}{Config.tile_convert(self.y + 2, True)}')
        except IndexError: pass

        return pos_moves

And if a move is within those possible moves, I move it:

 def move(self, pos):
        if pos in self.possible_moves():
            Config.board[self.y][self.x] = '___'

            self.x = Config.tile_convert(pos[0])
            self.y = Config.tile_convert(pos[1], True)

            Config.board[self.y][self.x] = self.pieceid

Yet, for some reason, the co-ordinates become out of wack and the piece fails to move when it should be able to:

knight1.move('f3')
time.sleep(2)
knight1.move('g5')
time.sleep(2)
knight1.move('h7')
time.sleep(2)
print(knight1.possible_moves())  # f8 should be in here
knight1.move('f8') #  fails

By printing coordinates I deduced that the problem is that they're not updating correctly. What's wrong with my logic?

This is my tile_convert() method:

def tile_convert(cls, x, disp=False):
        if not disp:
            if isinstance(x, str):
                return cls.letters.index(x)
            else:
                return cls.letters[x]
        else:
            return len(Config.board) - int(x)

Here's my full code if you'd like to run it:

import time

class Config:
    letters = tuple('abcdefghijklmnopqrstuvwxyz')


    @classmethod
    def new_board(cls, btype):
        def size(x):
            return [['___' for z in range(x)] for z in range(x)]

        if 'custom' in btype.lower():
            btype = int(btype.replace('custom', '').strip())
            cls.board = size(btype)
        elif btype.lower() == 'default':
            cls.board = size(8)
        elif btype.lower() == 'extended':
            cls.board = size(10)
        elif btype.lower() == 'small':
            cls.board = size(5)
        elif btype.lower() == 'max':
            cls.board = size(25)
        elif btype.lower() == 'min':
            cls.board = size(1)

    @classmethod
    def print_board(cls):
        def printl():
            for x in range(len(cls.board)):
                print(' '*6 + f'{cls.letters[x]}', end='')
            print('\n')

        printl()
        for x in range(len(cls.board)):
            print(f'{len(cls.board)-x}  {cls.board[x]}  {len(Config.board)-x}\n')
        printl()
        print('\n'*4)

    @classmethod
    def tile_convert(cls, x, disp=False):
        if not disp:
            if isinstance(x, str):
                return cls.letters.index(x)
            else:
                return cls.letters[x]
        else:
            return len(Config.board) - int(x)

class ChessPiece:
    def __init__(self, pos, color, num, piece):
        self.x = int(Config.tile_convert(pos[0]))
        self.y = len(Config.board) - int(pos[1])
        self.color = color
        self.pieceid = num
        self.set_id()
        self.create()

    def __str__(self):
        return self.__class__.__name__

    def set_id(self):
        if self.__class__.__name__ != "Knight":
            self.pieceid = f'{piece[0]}{self.pieceid}'
        else:
            self.pieceid = f'N{self.pieceid}'

        if self.color is not None:
            if self.color.lower() in ('black', 'white', 'b', 'w'):
                self.pieceid = self.color.lower()[0] + self.pieceid
                if self.color.lower() == 'b':
                    self.color = 'black'
                elif self.color.lower() == 'w':
                    self.color = 'white'
            else:
                self.color = None
                print("Invalid color input. Color not set.")
                self.set_id()
        else:
             self.pieceid = '_' + self.pieceid


    def create(self):
        Config.board[self.y][self.x] = self.pieceid

    def move(self, pos):
        if pos in self.possible_moves():
            Config.board[self.y][self.x] = '___'

            self.x = Config.tile_convert(pos[0])
            self.y = Config.tile_convert(pos[1], True)

            Config.board[self.y][self.x] = self.pieceid

            Config.print_board()

        else:
            print(f'Unable to move to {pos}')


    def get_info(self):
        print(f'{self.__class__.__name__}:\n')
        print('ID: ', self.pieceid)
        print('Position: ', Config.tile_convert(self.x), Config.tile_convert(self.y, True), sep='')
        print('Color: ', self.color)


class Knight(ChessPiece):
    def __init__(self, pos, color=None, num=''):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)


    def possible_moves(self):
        pos_moves = []

        # Up, Right (1 space, 2 spaces)
        try:
            if 1 <= self.x + 2 <= len(Config.board) and 1 <= self.y - 1 <= len(Config.board):
                if Config.board[self.x + 2][self.y - 1] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x + 2)}{Config.tile_convert(self.y - 1, True)}')
        except IndexError: pass

        #Up, Left
        try:
            if 1 <= self.x - 2 <= len(Config.board) and 1 <= self.y - 1 <= len(Config.board):
                if Config.board[self.x - 2][self.y - 1] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x - 2)}{Config.tile_convert(self.y - 1, True)}')
        except IndexError: pass

        # Down, Right
        try:
            if 1 <= self.x + 2 <= len(Config.board) and 1 <= self.y + 1 <= len(Config.board):
                if Config.board[self.x + 2][self.y + 1] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x + 2)}{Config.tile_convert(self.y + 1, True)}')
        except IndexError: pass

        #Down, Left
        try:
            if 1 <= self.x - 2 <= len(Config.board) and 1 <= self.y + 1 <= len(Config.board):
                if Config.board[self.x - 2][self.y + 1] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x - 2)}{Config.tile_convert(self.y + 1, True)}')
        except IndexError: pass

        # Right, Up
        try:
            if 1 <= self.x + 1 <= len(Config.board) and 1 <= self.y - 2 <= len(Config.board):
                if Config.board[self.x + 1][self.y - 2] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x + 1)}{Config.tile_convert(self.y - 2, True)}')
        except IndexError: pass

        # Right, Down
        try:
            if 1 <= self.x + 1 <= len(Config.board) and 1 <= self.y + 2 <= len(Config.board):
                if Config.board[self.x + 1][self.y + 2] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x + 1)}{Config.tile_convert(self.y + 2, True)}')
        except IndexError: pass

        #Left, Up
        try:
            if 1 <= self.x - 1 <= len(Config.board) and 1 <= self.y - 2 <= len(Config.board):
                print('Current: ', self.x, self.y)
                print('New: ', self.x - 1, self.y - 2)
                if Config.board[self.x - 1][self.y - 2] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x - 1)}{Config.tile_convert(self.y - 2, True)}')
        except IndexError: pass

        # Left, Down
        try:
            if 1 <= self.x - 1 <= len(Config.board) and 1 <= self.y + 2 <= len(Config.board):
                if Config.board[self.x - 1][self.y + 2] == '___':
                    pos_moves.append(f'{Config.tile_convert(self.x - 1)}{Config.tile_convert(self.y + 2, True)}')
        except IndexError: pass

        return pos_moves


Config.new_board('default')
Config.print_board()

knight1 = Knight('e1', color='w', num=1)

Config.print_board()

# knight1.get_info()

# print('\n\n\nPossible Moves:', knight1.possible_moves())

knight1.move('f3')
time.sleep(2)
knight1.move('g5')
time.sleep(2)
knight1.move('h7')
time.sleep(2)
print(knight1.possible_moves())
knight1.move('f8')

Solution

  • Your index checks in possible_moves are wrong, because list indexes go from 0 to len(Config.board)-1, not from 1 to len(Config.board).

    You can simplify the code greatly by using a loop to try all the different moves, and set variables so you don't have to keep repeating long expressions.

    def possible_moves(self):
        pos_moves = []
        boardlen = len(Config.board)
        for xoff, yoff in [(1, 2), (-1, 2), (1, -2), (-1, -2), (2, 1), (-2, 1), (2, -1), (-2, -1)]:
            newx = self.x + xoff
            newy = self.y + yoff
            if 0 <= newx < boardlen and 0 <= newy < boardlen and Config.board[newy][newx] == '___':
                pos_moves.append(f'{Config.tile_convert(newx)}{Config.tile_convert(newy, True)}')
        return pos_moves