-5

I am creating a small card game and I have this bug that I have tracked down to this array acting not how I would expect it to. I will not share all the code as there is too much of it.

#length of self.players = 2
self.players[0].deck.append("test");
print(self.players[1].deck); #output: ["test"], expected output: []

Any help on my issue would be appreciated.

Here is some background source code that may help:

This is part of the player class that contains the deck array

class Player:

turn = None;

power = 4;

deck = [];

cards_av = [];

def __init__(self, turn):

    self.turn = turn;

This is part of the 'engine' that I made to run a lot of game's logic

class Game_Engine:

objs = [];
background = [];
players = [];
camera = Camera(0, 0);

ui = [];

Thiss method is for inserting players, a player is a class (the Player class) and is appended to the players array

def insert_players(self, num):

    for i in range(num):

        self.players.append(Player(len(self.players)));

This method generates the map for the card game, the castles_placed variables is used for the beginning of the game

    def generate_map(self, parent):

    print("Generating " + str(parent.map_size * (parent.map_size * parent.ui_size)) + " bases");

    for i in range(parent.map_size):

        for j in range(parent.map_size - parent.ui_size):

            self.background.append(Base(i * parent.base_size, j * parent.base_size));

    parent.castles_placed = len(self.players);

    self.generate_game_ui(parent);

This method is for when a player skips their turn

    def skip(self):

    self.players[self.turn].power += self.skip_inc;
    self.turn += 1;

    for i in self.objs:

        i.unit_used = False;

    for i in self.objs:

        i.onturn(self.engine, self);

    self.prepped = None;
    self.highlighted = None;

    if self.turn >= len(self.players):

        self.turn = 0;

This is part of a class that creates a card that can be used in game.

class Card_Outline(UI_Object):

img = "imgs/Cards/Card_Outline.png";
img_surface = None;

def_val = False;

width = 40;
height = 72;

def __init__(self, x, y, unit, parent):

    self.x = x;
    self.y = y;

    self.unit = unit(self.x + (40 / 2) - (32 / 2), self.y + (72 / 2) - (32 / 2), -1);
    self.base_unit = unit;

    self.indent = len(parent.players[0].cards_av);

    for i in range(0, len(parent.players)):

        parent.players[i].cards_av.append(self.base_unit);

    cards = parent.players[parent.turn].deck;

    if len(cards) > 0:

        curr_card = cards[random.randint(0, len(cards) - 1)];

        self.unit = curr_card(self.x + (40 / 2) - (32 / 2), self.y + (72 / 2) - (32 / 2), -1);
        self.base_unit = curr_card;

    else:

        self.def_val = True;

    def onclick(self, parent, head):

    if self.base_unit.cost <= parent.players[parent.turn].power:

        parent.prepped = self.base_unit.copy();
        parent.players[parent.turn].power -= self.base_unit.cost;

        cards = parent.players[parent.turn].deck;

        curr_card = cards[random.randint(0, len(cards) - 1)];

        self.unit = curr_card(self.x + (40 / 2) - (32 / 2), self.y + (72 / 2) - (32 / 2), -1);
        self.base_unit = curr_card;

        parent.players[parent.turn].cards_av[self.indent] = self.base_unit.;

This runs every 'tick'; a tick happens 100 times a second in my game

    def tick(self, parent, head):

    self.base_unit = parent.players[parent.turn].cards_av[self.indent];
    self.unit = self.base_unit(self.x + (40 / 2) - (32 / 2), self.y + (72 / 2) - (32 / 2), -1);

    if self.base_unit == Unit:

        self.base_unit = parent.players[parent.turn].deck[random.randint(0, len(parent.players[parent.turn].deck) - 1)];

    parent.players[parent.turn].cards_av[self.indent] = self.base_unit;

    if self.def_val:

        cards = parent.players[parent.turn].deck;

        curr_card = cards[random.randint(0, len(cards) - 1)];

        self.unit = curr_card(self.x + (40 / 2) - (32 / 2), self.y + (72 / 2) - (32 / 2), -1);
        self.base_unit = curr_card;
        self.def_val = False;

        for i in range(0, len(parent.players)):

            parent.players[i].cards_av[self.indent] = self.base_unit;

This is the main game class, I run this file (which would normally contain some temporary code to run the game) to run the game

class Advancement:

engine = Game_Engine();

deck_limit = 8;

TPS = 100;
FPS = round(TPS / 30);

ticks = 0;

base_size = 32;
map_size = 20;
ui_size = 5;

castles_placed = 0;
wall_limit = 10;
walls_placed = 0;

WIDTH = base_size * map_size;
HEIGHT = base_size * map_size;

screen = pygame.display.set_mode((WIDTH, HEIGHT));

def __init__(self):

    pygame.init();
    pygame.font.init();

This is the main game in the Advancement class, it is quite big but I feel almost all of the code in this class is relevant

    def start(self):

    self.engine.running = True;

    self.engine.generate_map(self);

    #Temporary code, remove later after card selection UI has been developed

    for i in self.engine.cards:

        print(i.name);

    #Game loop

    while self.engine.running:

        self.ticks += 1;

        self.engine.tick(self);

        if self.ticks % self.FPS == 0:

            self.engine.display(self);

        if self.castles_placed == 0:

            self.walls_placed = len(self.engine.players) * self.wall_limit;
            self.castles_placed = -1;

        if self.castles_placed > 0:

            self.prep_unit(Castle);

        elif self.walls_placed > 0:

            self.prep_unit(Wall);

        elif self.engine.prepped != None:

            self.prep_unit(self.engine.prepped);

        self.engine.highlight_cursor(self);

        for e in pygame.event.get():

            if e.type == pygame.MOUSEBUTTONDOWN:

                if self.engine.highlighted != None:

                    appended = self.engine.highlighted;
                    appended.x = round(appended.x / self.base_size) * self.base_size;
                    appended.y = round(appended.y / self.base_size) * self.base_size;

                    append_bool = True;

                    for i in self.engine.objs:

                        if i.x == appended.x and i.y == appended.y:

                            append_bool = False;
                            break;

                    if append_bool:
                    
                        self.engine.objs.append(appended);
                        self.engine.objs[-1].onspawn(self.engine, self);

                    if self.engine.highlighted.__class__ == Castle(0, 0, 0).__class__:

                        self.castles_placed -= 1;

                    elif self.engine.highlighted.__class__ == Wall(0, 0, 0).__class__:

                        self.walls_placed -= 1;

                        if self.walls_placed % self.wall_limit != 0:
                        
                            self.engine.turn -= 1;
                    
                    self.engine.highlighted = None;
                    self.engine.prepped = None;
                    self.engine.turn += 1;

                    for i in self.engine.objs:

                        i.unit_used = False;

                    for i in self.engine.objs:

                        i.onturn(self.engine, self);

                if self.castles_placed == -1 and self.walls_placed == 0:

                    for i in self.engine.ui:

                        if not i.dead:

                            i.detect_click(self.engine, self);

                    for i in self.engine.objs:

                        if not i.dead:

                            i.detect_click(self.engine, self);

            if e.type == pygame.QUIT:

                pygame.quit();
                self.engine.running = False;

        if self.engine.turn >= len(self.engine.players):

            self.engine.turn = 0;

            for i in self.engine.players:

                i.power += self.engine.power_inc;
        
        time.sleep(1 / self.TPS);
opetch18
  • 49
  • 7
  • 3
    Does self players have two references to the same list? – Jared Smith Jul 02 '21 at 21:48
  • 1
    It seems like something may be happening to the array before the code you included. Please paste more of the (relevant) source code. – Igor Jul 02 '21 at 21:49
  • There is a lot of source code to go through but as far as I can find self.players does not have two references to the same list – opetch18 Jul 02 '21 at 21:55
  • You don't have to share all the code, just enough to let us reproduce the problem so we run it and see what's wrong. And yes, that means you might have to write some scaffolding around the code that you're sharing. – luther Jul 02 '21 at 21:55
  • I will start commenting some code – opetch18 Jul 02 '21 at 21:58
  • Please edit the code into your question. – luther Jul 02 '21 at 21:59
  • 1
    Sorry, I am inexpierenced – opetch18 Jul 02 '21 at 21:59
  • Does this answer your question? [Changing one list unexpectedly changes another, too](https://stackoverflow.com/questions/29785084/changing-one-list-unexpectedly-changes-another-too) – Michael Ruth Jul 02 '21 at 22:12

1 Answers1

2

Without much information, my guess is you are using the same list reference for all players. List is mutable and passed by reference. E.g.

old_list = [1, 2, 3]
new_list = old_list

# add an element to list
new_list.append('a')

print('New List:', new_list)
print('Old List:', old_list)

This will output -

Old List: [1, 2, 3, 'a']
New List: [1, 2, 3, 'a']

One has to use new_list = list.copy()instead to get a copy.