1

I am trying to create a basic Battleship game in Python using Tkinter.

Below is a very simplified version of my code. Essentially I am creating a 10*10 grid of buttons and positioning them using .grid. What I'd like to do is click one of those buttons and pass that buttons grid values (x, y) from the GameBoard class to the Battleship class to position the ship.

I have tried using self.row = row and self.column = column, however when I do this I immediately receive an attribute error, 'GameBoard' object has no attribute 'row'.

import tkinter as tk

class GameBoard:
    def __init__(self):
        self.mw = tk.Tk()
        self.size = 10

    def build_grid(self):
        for x in range(self.size):
            for y in range(self.size):
                self.button = tk.Button(self.mw, text = '', width = 2, height = 1,\
                command = lambda row = x, column = y: self.clicked(row, column))
                self.button.grid(row = x, column = y)
            
    def clicked(self, row, column):
        print(row, column)
        self.row = row
        self.column = column

class Battleship:
    def __init__(self, board):
        self.gboard = board

    def position_ship(self):
        x = self.gboard.row
        y = self.gboard.column
        for i in range (3):
            self.submarine = tk.Button(self.gboard.mw, background = "black", text = '',\
                                   width = 2, height = 1)
            self.submarine.grid(row = x, column = y)
              
def main():
    gboard = GameBoard()
    gboard.build_grid()
    bt = Battleship(gboard)
    bt.position_ship()    
main()
martineau
  • 119,623
  • 25
  • 170
  • 301
Mazda Man
  • 13
  • 3
  • 1
    When `bt.position_ship()` is called inside `main()`, `self.row` and `self.column` of `gboard` have not yet created because no button has been clicked. – acw1668 Dec 16 '21 at 12:15
  • @acw1668, how would I call `bt.position_ship()` after I've clicked a button? – Mazda Man Dec 16 '21 at 12:30

1 Answers1

0

As @acw1668 pointed out in a comment, the problem is the gboard attributes row and column haven't been created yet when you call bt.position_ship() in the main() function.

I don't know your overall game design, but a very simple way to fix that would be to assign a random board position to them in the GameBoard.__init__() method.

I've also modified the code to show how to call bt.position_ship() when a button is clicked. This is done by passing the BattleShip instance bt to the build_grid() function so it can be included in calls to the clicked() method, which can now call it when its called.

from random import randrange
import tkinter as tk

class GameBoard:
    def __init__(self):
        self.mw = tk.Tk()
        self.size = 10
        self.row = randrange(self.size)
        self.column = randrange(self.size)

    def build_grid(self, bt):
        for x in range(self.size):
            for y in range(self.size):
                self.button = tk.Button(self.mw, text='', width=2, height=1,
                                        command=lambda row=x, column=y:
                                            self.clicked(bt, row, column))
                self.button.grid(row=x, column=y)

    def clicked(self, bt, row, column):
        print(row, column)
        self.row = row
        self.column = column
        bt.position_ship()


class Battleship:
    def __init__(self, board):
        self.gboard = board

    def position_ship(self):
        x = self.gboard.row
        y = self.gboard.column
        for i in range (3):
            self.submarine = tk.Button(self.gboard.mw, background="black", text='',
                                       width=2, height=1)
            self.submarine.grid(row=x, column=y)


def main():
    gboard = GameBoard()
    bt = Battleship(gboard)
    gboard.build_grid(bt)
    bt.position_ship()
    tk.mainloop()

main()
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Thank you @martineau, I will implement this in my project now. – Mazda Man Dec 16 '21 at 13:19
  • You're welcome. Passing the Battleship around like may seem a little awkward, but it's better than using global variables — the lack of them being something that impressed me about your implementation — so I tried very hard to to not introduce any. – martineau Dec 16 '21 at 14:10