1

I have to check if some moves are legal on a chessboard.

The legal moves are:

  • for P (pikeman) only one square up, down, left, and right
  • for A (archer) the same as for P, plus one square in diagonal (up-left, up-right, down-left, down-right)
  • for K (knight) the same as for P, A, plus two squares up, down, left, right, and every diagonal direction

The algorithm I am trying to write is:

if the unit kind is P or A or K:
    if the position is up:
        move up
    if the position is down:
        move down
    if the position is left:
        move left
    if the position id right:
        move right
if the unit kind is A or K:
    if the position id up-left:
        move up-left
    if the position is up-right:
        move up-right
    if the position is down-left:
        move down_left
    if the position is down-right:
        move down right
if unit kind is K:
    if the position is two squares up:
        move two squares up
    if the position is two squares down:
        move two squares down
    if the position is two squares left:
        move two squares left
    if the position is two squares right:
        move two squares right
    if the position is two squares up-right:
        move two squares up-right
    if the position is two squares up-left:
        move two squares up-left
    if the position is two squares down-right:
        move two squares down-right
    if the position is two squares down-left:
        move two squares down-left

In my code, I am quite sure I have done exactly the same, but for some reason, everything works fine except for the fact that a P can also move one square in diagonal.

What am I doing wrong? I think the error has something to do with the logical operators 'and' and 'or', but I can't figure out what it is. I have already tried different solutions but there is always some unit that cannot move where it is actually supposed to.

Here is the simplest version of the code:

import pygame
import sys

# initialize pygame
pygame.init()

# set the window
window_size = (800, 800)
game_window = pygame.display.set_mode(window_size)
pygame.display.set_caption('My Canvas')

# define colours
colours = {
    'black': (0, 0, 0),
    'white': (255, 255, 255),
    'gold': (153, 153, 0),
    'green': (0, 180, 0)
}


# define classes
class Square:
    def __init__(self, colour, left, top):
        self.surface = game_window
        self.colour = colour
        self.width = 100
        self.height = 100
        self.left = left
        self.top = top
        self.rect = self.left, self.top, self.width, self.height

    def draw_square(self):
        pygame.draw.rect(self.surface, self.colour, self.rect)

    def get_square_position(self):
        return self.left, self.top

    def get_square_colour(self):
        return self.colour


class Unit:
    def __init__(self, colour, left, top):
        self.surface = game_window
        self.colour = colour
        self.left = left
        self.top = top
        self.kind = None

    def get_unit_position(self):
        return self.left, self.top

    def get_square_on(self):
        return pygame.Rect(self.left, self.top, 100, 100)


class Pikeman(Unit):
    def __init__(self, colour, left, top):
        super().__init__(colour, left, top)
        self.shape_left = self.left + 10
        self.shape_top = self.top + 10
        self.current_position = Pikeman.get_unit_position(self)
        self.left = left
        self.top = top
        self.width = 80
        self.height = 80
        self.square_on = pygame.Rect(left, top, 100, 100)
        self.kind = 'P'

    def draw_unit(self):
        pygame.draw.rect(self.surface, self.colour, (self.shape_left, self.shape_top, self.width, self.height))
        pygame.draw.rect(self.surface, colours.get('gold'),
                         (self.shape_left, self.shape_top, self.width, self.height), 3)
        pygame.draw.rect(self.surface, colours.get('gold'),
                         (self.shape_left + 5, self.shape_top + 5, self.width - 10, self.height - 10), 2)
        pygame.font.init()
        my_font_size = 50
        my_font = pygame.font.SysFont('Time New Roman', my_font_size)
        text_surface = my_font.render('P', 1, colours.get('gold'))
        x_centre = self.shape_left + self.width / 2
        y_centre = self.shape_top + self.height / 2
        centre_text = text_surface.get_rect(center=(x_centre, y_centre))
        self.surface.blit(text_surface, centre_text)

    def set_unit_position(self, left, top):
        self.shape_left = left + 10
        self.shape_top = top + 10
        self.left = left
        self.top = top
        self.square_on = pygame.Rect(left, top, 100, 100)

        draw_chessboard()
        for white_unit in white_army:
            if white_unit.get_unit_position() is not selected_unit.new_position:
                white_unit.draw_unit()
        for black_unit in black_army:
            if black_unit.get_unit_position() is not selected_unit.get_unit_position():
                black_unit.draw_unit()
        self.draw_unit()


class Archer(Unit):
    def __init__(self, colour, left, top):
        super().__init__(colour, left, top)
        self.left = left
        self.top = top
        self.width = 80
        self.height = 80
        self.square_on = pygame.Rect(left, top, 100, 100)
        if self.colour is colours.get('white'):
            self.shape_left = self.left + 10, self.top + 90
            self.shape_top = self.left + 10 + (((self.left + 90) - (self.left + 10)) / 2), self.top + 10
            self.shape_right = self.left + 90, self.top + 90
            self.current_position = Archer.get_unit_position(self)
        else:
            self.shape_left = self.left + 10, self.top + 10
            self.shape_top = self.left + 10 + (((self.left + 90) - (self.left + 10)) / 2), self.top + 90
            self.shape_right = self.left + 90, self.top + 10
            self.current_position = Archer.get_unit_position(self)
        self.kind = 'A'

    def draw_unit(self):
        if self.colour is colours.get('white'):
            pygame.draw.polygon(self.surface, self.colour, (self.shape_left, self.shape_top, self.shape_right))
            pygame.draw.polygon(self.surface, colours.get('gold'),
                                (self.shape_left, self.shape_top, self.shape_right), 3)
            pygame.draw.polygon(self.surface, colours.get('gold'),
                                ((self.left + 20, self.top + 85),
                                 (self.left + 50 + (((self.left + 80) - (self.left + 80)) / 2), self.top + 20),
                                 (self.left + 80, self.top + 85)), 2)
            pygame.font.init()
            my_font_size = 50
            my_font = pygame.font.SysFont('Time New Roman', my_font_size)
            text_surface = my_font.render('A', 1, colours.get('gold'))
            x_centre = self.left + 50
            y_centre = self.top + 60
            centre_text = text_surface.get_rect(center=(x_centre, y_centre))
            self.surface.blit(text_surface, centre_text)
        else:
            pygame.draw.polygon(self.surface, self.colour, (self.shape_left, self.shape_top, self.shape_right))
            pygame.draw.polygon(self.surface, colours.get('gold'), (self.shape_left, self.shape_top, self.shape_right),
                                3)
            pygame.draw.polygon(self.surface, colours.get('gold'),
                                ((self.left + 20, self.top + 15),
                                 (self.left + 50 + (((self.left + 80) - (self.left + 80)) / 2), self.top + 80),
                                 (self.left + 80, self.top + 15)), 2)
            pygame.font.init()
            my_font_size = 50
            my_font = pygame.font.SysFont('Time New Roman', my_font_size)
            text_surface = my_font.render('A', 1, colours.get('gold'))
            x_centre = self.left + 50
            y_centre = self.top + 35
            centre_text = text_surface.get_rect(center=(x_centre, y_centre))
            self.surface.blit(text_surface, centre_text)

    def set_unit_position(self, left, top):
        self.left = left
        self.top = top
        self.square_on = pygame.Rect(left, top, 100, 100)
        if self.colour is colours.get('white'):
            self.shape_left = self.left + 10, self.top + 90
            self.shape_top = self.left + 10 + (((self.left + 90) - (self.left + 10)) / 2), self.top + 10
            self.shape_right = self.left + 90, self.top + 90
        else:
            self.shape_left = self.left + 10, self.top + 10
            self.shape_top = self.left + 10 + (((self.left + 90) - (self.left + 10)) / 2), self.top + 90
            self.shape_right = self.left + 90, self.top + 10

        draw_chessboard()
        for white_unit in white_army:
            if white_unit.get_unit_position() != selected_unit.new_position:
                white_unit.draw_unit()
        for black_unit in black_army:
            if black_unit.get_unit_position() is not selected_unit.get_unit_position():
                black_unit.draw_unit()
        self.draw_unit()


class Knight(Unit):
    def __init__(self, colour, left, top):
        super().__init__(colour, left, top)
        self.left = left
        self.top = top
        self.width = 80
        self.height = 8
        self.shape_left = self.left + 50
        self.shape_top = self.top + 50
        self.square_on = pygame.Rect(left, top, 100, 100)
        self.kind = 'K'

    def draw_unit(self):
        pygame.draw.circle(self.surface, self.colour, (self.shape_left, self.shape_top), 40)
        pygame.draw.circle(self.surface, colours.get('gold'), (self.shape_left, self.shape_top), 40, 3)
        pygame.draw.circle(self.surface, colours.get('gold'), (self.shape_left, self.shape_top), 32, 1)
        pygame.font.init()
        my_font_size = 50
        my_font = pygame.font.SysFont('Time New Roman', my_font_size)
        text_surface = my_font.render('K', 1, colours.get('gold'))
        x_centre = self.shape_left
        y_centre = self.shape_top
        centre_text = text_surface.get_rect(center=(x_centre, y_centre))
        self.surface.blit(text_surface, centre_text)

    def set_unit_position(self, left, top):
        self.shape_left = left + 50
        self.shape_top = top + 50
        self.left = left
        self.top = top
        self.square_on = pygame.Rect(left, top, 100, 100)

        draw_chessboard()
        for white_unit in white_army:
            if white_unit.get_unit_position() is not selected_unit.new_position:
                white_unit.draw_unit()
        for black_unit in black_army:
            if black_unit.get_unit_position() is not selected_unit.get_unit_position():
                black_unit.draw_unit()
        self.draw_unit()


# define functions
def draw_chessboard():
    for left in range(0, 800, 100):
        for top in range(0, 800, 100):
            if left in range(0, 800, 200) and top in range(0, 800, 200):
                colour = colours.get('white')
                square_object = Square(colour, left, top)
                square_object.draw_square()
            if left in range(100, 800, 200) and top in range(100, 800, 200):
                colour = colours.get('white')
                square_object = Square(colour, left, top)
                square_object.draw_square()
            if left in range(100, 800, 200) and top in range(0, 800, 200):
                colour = colours.get('black')
                square_object = Square(colour, left, top)
                square_object.draw_square()
            if left in range(0, 800, 200) and top in range(100, 800, 200):
                colour = colours.get('black')
                square_object = Square(colour, left, top)
                square_object.draw_square()


def highlight_square(square_on):
    pygame.draw.rect(game_window, colours.get('green'), square_on, 5)


def is_legal_move(selected_unit):
    squares_occupied = []
    for white_unit_square in white_army:
        squares_occupied.append(white_unit_square.get_square_on())
    for black_unit_square in black_army:
        squares_occupied.append(black_unit_square.get_square_on())
    for square_occupied in squares_occupied:
        if square_occupied.collidepoint(new_position[0], new_position[1]):
            print('cannot move here, this square if occupied by a friendly unit!')
            return False
    # legal moves common between pikemen, archers, and knights
    if selected_unit.kind is 'P' or 'A' or 'K':
        # legal up move
        if (current_position[0] < new_position[0] < current_position[0] + 100) and \
                (current_position[1] - 100) < new_position[1] < (current_position[1]):
            if 0 < new_position[1] < 800:
                selected_unit.new_position = current_position[0], current_position[1] - 100
                print('legal up move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal down move
        if (current_position[0] < new_position[0] < current_position[0] + 100) and \
                (current_position[1] + 200) > new_position[1] > (current_position[1] + 100):
            if 0 < new_position[1] < 800:
                selected_unit.new_position = current_position[0], current_position[1] + 100
                print('legal down move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal left move
        if (current_position[0] - 100 < new_position[0] < current_position[0]) and \
                (current_position[1] + 100) > new_position[1] > (current_position[1]):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] - 100, current_position[1]
                print('legal left move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal right move
        if (current_position[0] + 100 < new_position[0] < current_position[0] + 200) and \
                (current_position[1] + 100) > new_position[1] > (current_position[1]):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] + 100, current_position[1]
                print('legal right move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
    # legal moves in common between archers and knights
    if selected_unit.kind is 'K' or 'A':
        # legal left-up move
        if (current_position[0] - 100 < new_position[0] < current_position[0]) and \
             (current_position[1] - 100 < new_position[1] < current_position[1]):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] - 100, current_position[1] - 100
                print('legal left-up move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal right-up move
        if (current_position[0] + 100 < new_position[0] < current_position[0] + 200) and \
                (current_position[1] - 100 < new_position[1] < current_position[1]):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] + 100, current_position[1] - 100
                print('legal right-up move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal left-backward move
        if (current_position[0] - 100 < new_position[0] < current_position[0]) and \
                (current_position[1] + 100 < new_position[1] < current_position[1] + 200):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] - 100, current_position[1] + 100
                print('legal left-down move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal right-backward move
        if (current_position[0] + 100 < new_position[0] < current_position[0] + 200) and \
                (current_position[1] + 100 < new_position[1] < current_position[1] + 200):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] + 100, current_position[1] + 100
                print('legal right-down move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
    # legal moves exclusive to knights
    if selected_unit.kind is 'K':
        # legal long up move
        if (current_position[0] < new_position[0] < current_position[0] + 100) and \
                (current_position[1] - 200) < new_position[1] < (current_position[1] - 100):
            if 0 < new_position[1] < 800:
                selected_unit.new_position = current_position[0], current_position[1] - 200
                print('legal long up move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal long down move
        if (current_position[0] < new_position[0] < current_position[0] + 100) and \
                current_position[1] + 200 < new_position[1] < (current_position[1] + 300):
            if 0 < new_position[1] < 800:
                selected_unit.new_position = current_position[0], current_position[1] + 200
                print('legal long down move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal long left move
        if (current_position[0] - 200 < new_position[0] < current_position[0] - 100) and \
                current_position[1] < new_position[1] < (current_position[1] + 100):
            if 0 < new_position[1] < 800:
                selected_unit.new_position = current_position[0] - 200, current_position[1]
                print('legal long left move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
            # legal long right move
        if (current_position[0] + 200 < new_position[0] < current_position[0] + 300) and \
                current_position[1] < new_position[1] < (current_position[1] + 100):
            if 0 < new_position[1] < 800:
                selected_unit.new_position = current_position[0] + 200, current_position[1]
                print('legal long left move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal left-up long move
        if (current_position[0] - 200 < new_position[0] < current_position[0] - 100) and \
                (current_position[1] - 200 < new_position[1] < current_position[1] - 100):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] - 200, current_position[1] - 200
                print('legal left-up long move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal right-up long move
        if (current_position[0] + 200 < new_position[0] < current_position[0] + 300) and \
                (current_position[1] - 200 < new_position[1] < current_position[1] - 100):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] + 200, current_position[1] - 200
                print('legal right-up long move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal left-down long move
        if (current_position[0] - 200 < new_position[0] < current_position[0] - 100) and \
                (current_position[1] + 200 < new_position[1] < current_position[1] + 300):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] - 200, current_position[1] + 200
                print('legal left-down long move to ' + str(selected_unit.new_position))
                return True
            else:
                return False
        # legal right-down long move
        if (current_position[0] + 200 < new_position[0] < current_position[0] + 300) and \
                (current_position[1] + 200 < new_position[1] < current_position[1] + 300):
            if 0 < new_position[0] < 800:
                selected_unit.new_position = current_position[0] + 200, current_position[1] + 200
                print('legal right-down long move to ' + str(selected_unit.new_position))
                return True
            else:
                return False


def get_current_position():
    return selected_unit.square_on[0], selected_unit.square_on[1]


def get_new_position():
    return selected_unit.get_unit_position()


# initialise events
selected_unit = None
game_state = 'no unit selected'
turn = 'white army'
print('---' + str(turn).upper() + ', IS YOUR TURN---')

# initialise board and units
draw_chessboard()
white_army = []
for x in range(200, 600, 100):
    y = 700
    a_pikeman = Pikeman(colours.get('white'), x, y,)
    a_pikeman.draw_unit()
    white_army.append(a_pikeman)
for x in range(200, 600, 100):
    y = 600
    an_archer = Archer(colours.get('white'), x, y, )
    an_archer.draw_unit()
    white_army.append(an_archer)
for x in range(100, 700, 500):
    y = 600
    a_knight = Knight(colours.get('white'), x, y)
    a_knight.draw_unit()
    white_army.append(a_knight)
black_army = []
for x in range(200, 600, 100):
    y = 0
    a_pikeman = Pikeman(colours.get('black'), x, y,)
    a_pikeman.draw_unit()
    black_army.append(a_pikeman)
for x in range(200, 600, 100):
    y = 100
    an_archer = Archer(colours.get('black'), x, y, )
    an_archer.draw_unit()
    black_army.append(an_archer)
for x in range(100, 700, 500):
    y = 100
    a_knight = Knight(colours.get('black'), x, y)
    a_knight.draw_unit()
    black_army.append(a_knight)
armies = white_army + black_army

# main loop
while 1:

    # event loop
    for event in pygame.event.get():
        if event.type is pygame.QUIT:
            sys.exit()

        # mouse click handling
        if event.type == pygame.MOUSEBUTTONDOWN:
            new_position = pygame.mouse.get_pos()

            # unit not selected
            if game_state is 'no unit selected':
                if turn is 'white army':
                    for white_unit in white_army:
                        if white_unit.square_on.collidepoint(new_position[0], new_position[1]):
                            selected_unit = white_unit
                            game_state = 'unit selected'
                            print(str(game_state) + ', this unit is ' + str(selected_unit.kind))

                elif turn is 'black army':
                    for black_unit in black_army:
                        if black_unit.square_on.collidepoint(new_position[0], new_position[1]):
                            selected_unit = black_unit
                            game_state = 'unit selected'
                            print(str(game_state) + 'this unit is ' + str(selected_unit.kind))

            elif game_state == 'unit selected':
                if turn is 'white army':
                    current_position = selected_unit.square_on
                    if is_legal_move(selected_unit):
                        game_state = 'unit movement allowed'
                        turn = 'black army'
                    else:
                        print('move not allowed, choose another square')
                elif turn is 'black army':
                    current_position = selected_unit.square_on
                    if is_legal_move(selected_unit):
                        game_state = 'unit movement allowed'
                        turn = 'white army'
                    else:
                        print('move not allowed, choose another square')

    # application loop

    # highlight square
    if game_state == 'unit selected':
        highlight_square(selected_unit.square_on)

    # move unit
    if game_state == 'unit movement allowed':
        selected_unit.set_unit_position(selected_unit.new_position[0], selected_unit.new_position[1])
        game_state = 'no unit selected'
        print('unit moved')
        print('---' + str(turn).upper() + ', IS YOUR TURN---')
        selected_unit = None

    pygame.display.update()

1 Answer 1

2
if selected_unit.kind is 'P' or 'A' or 'K':

should be

if selected_unit.kind == 'P' or selected_unit.kind == 'A' or selected_unit.kind == 'K':

and so on.

To write it more succinctly, you could do something like

if selected_unit.kind in 'PAK':
Sign up to request clarification or add additional context in comments.

1 Comment

Some clarification why this doesn't work. If statements will go through for any non zero value. It treats text as values. Given a kind = 'P' : if selected_unit.kind is 'K' or 'A': This will run as False or 'A', which since it is an or statement will combined give you 'A'. The if statement will consider 'A' as True.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.