Python Forum
Battleships game in python with tkinter
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Battleships game in python with tkinter
#1
I am doing my best to create a battleships game in python with tkinter.

Currently the board consists of a list with lists of "~" and "around" it is a border of "#". The border is there to check that a ship can be placed on the board and not extend beyond the board. I know this seems redundant, which is why I humbly ask for improvements.

I am having two major problems with this though:

I have a hunch that I should be implementing classes but I am very new to them so I'd greatly appreciate some guidance.

My "AI" function (ai_shoots) is currently shooting on the same field that the player is shooting at, which it obviously shouldn't do. Instead it should shoot on your board. Press the 1 player button to play against the AI.

from tkinter import *
from tkinter import font
import tkinter.messagebox
from functools import partial
import random
import time
import sys
import os

root = Tk()
root.wm_title("BATTLESHIPS")
root.configure(background='gray19')
font1 = font.Font(family='Helvetica', size=12, weight='bold')
font_big = font.Font(family='Helvetica', size=16, weight='bold')
font_normal = font.Font(family='Helvetica', size=10, weight='normal')

ships = {"Aircraft Carrier": 4, "Battleship": 3, "Submarine": 2, "Destroyer": 1}
AI = False


def restart_program():
    python = sys.executable
    os.execl(python, python, *sys.argv)


def player_board():
    board = []
    t = []
    t += (10 + 2) * ['# ']
    board.append(t)  # Övre ram

    rad = ['# ']  # Vänster ram
    for r in range(0, 10):
        rad.append("~ ")
    rad.append('# ')  # Höger ram
    for k in range(0, 10):
        board.append(list(rad))

    board.append(t)  # Undre ram
    return board


def place_ship(ship, board):
    # w = 0  # Håller igång loopen
    while True:
        checkcoords = []
        x = random.randint(1, 10)  # Genererad x-koordinat
        y = random.randint(1, 10)  # Genererad y-koordinat
        o = random.randint(0, 1)  # Väljer placeringssätt
        if o == 0:
            ori = "v"  # Vertikalt
        else:
            ori = "h"  # Horisontellt
        if ori == "v" and y + ships[ship] > 10:
            pass
            # w = 0  # Säkerställer att båten kan placeras inom spelplanen
        elif ori == "h" and x + ships[ship] > 10:
            pass
            # w = 0
        else:
            if ori == "v":
                for i in range(-1, (ships[ship] + 1)):
                    for j in range(-1, 2):
                        checkcoords.append(board[y + i][x + j])
                if ': ' not in checkcoords:
                    for i in range(ships[ship]):
                        board[y + i][x] = ': '
                    break
            #                else:
            #                    w = 0
            elif ori == "h":
                for i in range(-1, (ships[ship] + 1)):
                    for j in range(-1, 2):
                        checkcoords.append(board[y + j][x + i])
                if ': ' not in checkcoords:
                    for i in range(ships[ship]):
                        board[y][x + i] = ': '
                    break


#                else:
#                    w = 0


def place_all_ships(board):
    for ship in ships:
        for antal in range(0, (5 - ships[ship])):
            place_ship(ship, board)


def popupwindow(msg):
    answer = tkinter.messagebox.askquestion("Game Over", msg + " Would you like to play again?")
    if answer == "yes":
        restart_program()
    elif answer == "no":
        quit()


def nr_players(number):
    global AI
    if number == 1:
        player2_or_AI.set("AI")
        AI = True
    else:
        player2_or_AI.set("Player 2")


info = StringVar()
player2_or_AI = StringVar()


def side_labels():
    # info = StringVar()
    Label(root, text="BATTLESHIPS", fg="white", bg="gray19", font=font_big).grid(row=0, column=10, columnspan=9)
    Label(root, textvariable=info, fg="white", bg="gray19", font=font1).grid(row=12, column=6, columnspan=18)

    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=_, column=0)
    Button(root, width=7, height=1, text="1 Player", font=font1, fg="white", activebackground="gray19",
           bg="gray19", command=lambda: nr_players(1)).grid(row=2, column=1)
    Button(root, width=7, height=1, text="2 Players", font=font1, fg="white", activebackground="gray19",
           bg="gray19", command=lambda: nr_players(2)).grid(row=3, column=1)
    Label(root, text="Get 20 hits to win", font=font_normal, fg="white", bg="gray19").grid(row=5, column=1)
    Label(root, text="1 Battleship 4 units", font=font_normal, fg="white", bg="gray19").grid(row=6, column=1)
    Label(root, text="2 Battleships 3 units", font=font_normal, fg="white", bg="gray19").grid(row=7, column=1)
    Label(root, text="3 Battleships 2 units", font=font_normal, fg="white", bg="gray19").grid(row=8, column=1)
    Label(root, text="4 Battleships 1 unit  ", font=font_normal, fg="white", bg="gray19").grid(row=9, column=1)

    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=_, column=2)

    for _ in range(10):
        Label(root, width=20, text="   ", bg="gray19").grid(row=_, column=25)


def ai_shoots(y_coord, x_coord, all_buttons, player_1_board, ai_score):
    # print("yes")
    if ai_score == 20:
        popupwindow("The computer has won.")
    if player_1_board[y_coord][x_coord] == ': ':
        ai_score += 1
        player_1_board[y_coord][x_coord] = 'X '
        all_buttons[y_coord - 1][x_coord - 1].configure(text="X", fg="black", bg="red3")
        if player_1_board[y_coord - 1][x_coord] == ': ':
            ai_shoots(y_coord - 1, x_coord, all_buttons, player_1_board, ai_score)
        elif player_1_board[y_coord + 1][x_coord] == ': ':
            ai_shoots(y_coord + 1, x_coord, all_buttons, player_1_board, ai_score)
        elif player_1_board[y_coord][x_coord - 1] == ': ':
            ai_shoots(y_coord, x_coord - 1, all_buttons, player_1_board, ai_score)
        elif player_1_board[y_coord][x_coord + 1] == ': ':
            ai_shoots(y_coord, x_coord + 1, all_buttons, player_1_board, ai_score)
        else:
            x = random.randint(1, 10)
            y = random.randint(1, 10)
            ai_shoots(y, x, all_buttons, player_1_board, ai_score)
    elif player_1_board[y_coord][x_coord] == 'X ' or player_1_board[y_coord][x_coord] == 'O ':
        x = random.randint(1, 10)
        y = random.randint(1, 10)
        ai_shoots(y, x, all_buttons, player_1_board, ai_score)
    else:
        player_1_board[y_coord][x_coord] = 'O '
        all_buttons[y_coord - 1][x_coord - 1].configure(text="O", fg="white")


def hit_or_miss(a, b, board, all_buttons, info, player, player_1_hits, player_2_hits, ai_score):
    global AI
    # print(player)
    if board[a + 1][b + 1] == 'O ' or board[a + 1][b + 1] == 'X ':  # Redan skjutit
        info.set("You have already fired there, " + player + "!")

    elif board[a + 1][b + 1] == ': ':  # Träff
        info.set("A hit, nice shot " + player + "!")
        board[a + 1][b + 1] = 'X '
        all_buttons[a][b].configure(text="X", fg="black", bg="red3", activebackground="red3")
        if player == "player 1":
            player_1_hits += 1
        else:
            player_2_hits += 1

    else:  # Miss
        info.set("Seems like you missed that one, " + player + "!")
        board[a + 1][b + 1] = 'O '
        all_buttons[a][b].configure(text="O", fg="White", activeforeground="white")
        # print(AI)
        if AI:
            x = random.randint(0, 10)
            y = random.randint(0, 10)
            ai_shoots(y, x, all_buttons, board, ai_score)
    if player_1_hits == 20 or player_2_hits == 20:
        popupwindow(player + " has won!")


def side(player, allbuttons):
    print(player)
    if player == "player 1":
        for row in range(10):
            for column in range(10):
                allbuttons[row][column].grid(row=1 + row, column=4 + column)

        label2 = Label(root, text="Player 1", font=font1, fg="white", bg="gray19")
        label2.grid(row=11, column=4, columnspan=10)
    else:
        for row in range(10):
            for column in range(10):
                allbuttons[row][column].grid(row=1 + row, column=15 + column)

        label3 = Label(root, textvariable=player2_or_AI, font=font1, fg="white", bg="gray19")
        label3.grid(row=11, column=15, columnspan=10)


def board_buttons(board, info, player, player_1_hits, player_2_hits, ai_score):
    allbuttons = []
    a = 0
    print(AI)
    for i in range(10):
        b = 0
        buttons = []
        for j in range(10):
            button = Button(root, width=2, height=1, font=font1, bg="sky blue", activebackground="sky blue",
                            command=partial(hit_or_miss, a, b, board, allbuttons,
                                            info, player, player_1_hits, player_2_hits, ai_score))
            buttons.append(button)
            b += 1
        allbuttons.append(list(buttons))
        a += 1

    side(player, allbuttons)


def middle_board_space():
    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=1 + _, column=14)


def main():
    player_1_hits = 0
    player_2_hits = 0
    ai_hits = 0

    player_1_board = player_board()
    player_2_board = player_board()

    place_all_ships(player_1_board)  # Sätter ut alla skeppen
    place_all_ships(player_2_board)

    info = StringVar()
    side_labels()

    board_buttons(player_1_board, info, "player 1", player_1_hits, player_2_hits, ai_hits)
    middle_board_space()
    board_buttons(player_2_board, info, "player 2", player_1_hits, player_2_hits, ai_hits)


main()
root.mainloop()
############################################
Reply
#2
Hi :)
I had do use some different imports for Tkinter to get it running.
The problem with the AI was that you passed ai_shoot the same board as you used for player_2. And on top of that the all_button array only holds the buttons from the field where it has been triggered. Since ai_shoot only is triggered when you click a button on the player_2 field, the buttons of that field are used. You can bypass this by using a global array where you store all buttons from both fields. After creation, the buttons of player_1 would be at the first index. You should consider blocking the click event on player_1 field while AI is ingame. Since clicking these buttons still work, they would trigger the AI as well.

Objects are the most important part of python. You worked with some objects yourself while programming this game. You can imagine it that way: Objects are build out of classes, like an actual chair is build by using a blueprint. Since there are different kind of chairs, each chair may have different attributes. There are chairs without a back piece or chairs with just 3 legs. There are even chairs with wheels. In your case you could make your players as Objects of the player class. Each player has a score, a name, board and ships. The AI even has the right to act without a human.
The overall board itself could be a class which has different methods like: hit_or_miss and attributes like all buttons and a width/height.
This book is good and captures everything:
http://books.tarsoit.com/Python%203%20Ob...dition.pdf (19.09.2018)
Though it is quite a lot if you need it asap :D Python2 and Python3 differ in some points but the important things you need right now are identical.
These got the essentials:
https://www.johnny-lin.com/pyintro/ed01/...s/ch07.pdf (19.09.2018)
https://pdfs.semanticscholar.org/present...051677.pdf (19.09.2018)

If I may, I would like to give you some programming tips:
1. ALWAYS comment in english. I know that it is quicker and easier to comment in you native language, but sharing the code is quite difficult. The guy on the other end of the screen has to find out what you did. Also not each symbol is used in each language. I for myself had the problem that your comments were not in ascii format. It is also great for improving english skills and writing english commands right away, if you want to work in the IT or CS field.
2. Do yourself a favour, and comment more ;) It is horrible to not look at your own code for some days and when you get back on it you have to figure out what you did.

from Tkinter import *
from tkFont import Font
import tkMessageBox
from functools import partial
import random
import time
import sys
import os

root = Tk()
root.wm_title("BATTLESHIPS")
root.configure(background='gray19')
font1 = Font(family='Helvetica', size=12, weight='bold')
font_big = Font(family='Helvetica', size=16, weight='bold')
font_normal = Font(family='Helvetica', size=10, weight='normal')

ships = {"Aircraft Carrier": 4, "Battleship": 3, "Submarine": 2, "Destroyer": 1}
AI = False


def restart_program():
    python = sys.executable
    os.execl(python, python, *sys.argv)


def player_board():
    board = []
    t = []
    t += (10 + 2) * ['# ']
    board.append(t)

    rad = ['# ']
    for r in range(0, 10):
        rad.append("~ ")
    rad.append('# ')
    for k in range(0, 10):
        board.append(list(rad))

    board.append(t)
    return board


def place_ship(ship, board):
    # w = 0
    while True:
        checkcoords = []
        x = random.randint(1, 10)
        y = random.randint(1, 10)
        o = random.randint(0, 1)
        if o == 0:
            ori = "v"  # Vertikalt
        else:
            ori = "h"  # Horisontellt
        if ori == "v" and y + ships[ship] > 10:
            pass
            # w = 0
        elif ori == "h" and x + ships[ship] > 10:
            pass
            # w = 0
        else:
            if ori == "v":
                for i in range(-1, (ships[ship] + 1)):
                    for j in range(-1, 2):
                        checkcoords.append(board[y + i][x + j])
                if ': ' not in checkcoords:
                    for i in range(ships[ship]):
                        board[y + i][x] = ': '
                    break
            #                else:
            #                    w = 0
            elif ori == "h":
                for i in range(-1, (ships[ship] + 1)):
                    for j in range(-1, 2):
                        checkcoords.append(board[y + j][x + i])
                if ': ' not in checkcoords:
                    for i in range(ships[ship]):
                        board[y][x + i] = ': '
                    break


#                else:
#                    w = 0


def place_all_ships(board):
    for ship in ships:
        for antal in range(0, (5 - ships[ship])):
            place_ship(ship, board)


def popupwindow(msg):
    answer = tkMessageBox.askquestion("Game Over", msg + " Would you like to play again?")
    if answer == "yes":
        restart_program()
    elif answer == "no":
        quit()


def nr_players(number):
    global AI
    if number == 1:
        player2_or_AI.set("AI")
        AI = True
    else:
        player2_or_AI.set("Player 2")


info = StringVar()
player2_or_AI = StringVar()
every_button = []


def side_labels():
    # info = StringVar()
    Label(root, text="BATTLESHIPS", fg="white", bg="gray19", font=font_big).grid(row=0, column=10, columnspan=9)
    Label(root, textvariable=info, fg="white", bg="gray19", font=font1).grid(row=12, column=6, columnspan=18)

    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=_, column=0)
    Button(root, width=7, height=1, text="1 Player", font=font1, fg="white", activebackground="gray19",
           bg="gray19", command=lambda: nr_players(1)).grid(row=2, column=1)
    Button(root, width=7, height=1, text="2 Players", font=font1, fg="white", activebackground="gray19",
           bg="gray19", command=lambda: nr_players(2)).grid(row=3, column=1)
    Label(root, text="Get 20 hits to win", font=font_normal, fg="white", bg="gray19").grid(row=5, column=1)
    Label(root, text="1 Battleship 4 units", font=font_normal, fg="white", bg="gray19").grid(row=6, column=1)
    Label(root, text="2 Battleships 3 units", font=font_normal, fg="white", bg="gray19").grid(row=7, column=1)
    Label(root, text="3 Battleships 2 units", font=font_normal, fg="white", bg="gray19").grid(row=8, column=1)
    Label(root, text="4 Battleships 1 unit  ", font=font_normal, fg="white", bg="gray19").grid(row=9, column=1)

    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=_, column=2)

    for _ in range(10):
        Label(root, width=20, text="   ", bg="gray19").grid(row=_, column=25)


def ai_shoots(y_coord, x_coord, player_1_board, ai_score):
    # print("yes")
    if ai_score == 20:
        popupwindow("The computer has won.")
    if player_1_board[y_coord][x_coord] == ': ':
        ai_score += 1
        player_1_board[y_coord][x_coord] = 'X '
        every_button[0][y_coord - 1][x_coord - 1].configure(text="X", fg="black", bg="red3")
        if player_1_board[y_coord - 1][x_coord] == ': ':
            ai_shoots(y_coord - 1, x_coord, all_buttons, player_1_board, ai_score)
        elif player_1_board[y_coord + 1][x_coord] == ': ':
            ai_shoots(y_coord + 1, x_coord, all_buttons, player_1_board, ai_score)
        elif player_1_board[y_coord][x_coord - 1] == ': ':
            ai_shoots(y_coord, x_coord - 1, all_buttons, player_1_board, ai_score)
        elif player_1_board[y_coord][x_coord + 1] == ': ':
            ai_shoots(y_coord, x_coord + 1, all_buttons, player_1_board, ai_score)
        else:
            x = random.randint(1, 10)
            y = random.randint(1, 10)
            ai_shoots(y, x, all_buttons, player_1_board, ai_score)
    elif player_1_board[y_coord][x_coord] == 'X ' or player_1_board[y_coord][x_coord] == 'O ':
        x = random.randint(1, 10)
        y = random.randint(1, 10)
        ai_shoots(y, x, all_buttons, player_1_board, ai_score)
    else:
        player_1_board[y_coord][x_coord] = 'O '
        every_button[0][y_coord - 1][x_coord - 1].configure(text="O", fg="white")


def hit_or_miss(a, b, board, all_buttons, info, player, player_1_hits, player_2_hits, ai_score, board2):
    global AI
    # print(player)
    if board[a + 1][b + 1] == 'O ' or board[a + 1][b + 1] == 'X ':
        info.set("You have already fired there, " + player + "!")

    elif board[a + 1][b + 1] == ': ':
        info.set("A hit, nice shot " + player + "!")
        board[a + 1][b + 1] = 'X '
        all_buttons[a][b].configure(text="X", fg="black", bg="red3", activebackground="red3")
        if player == "player 1":
            player_1_hits += 1
        else:
            player_2_hits += 1

    else:  # Miss
        info.set("Seems like you missed that one, " + player + "!")
        board[a + 1][b + 1] = 'O '
        all_buttons[a][b].configure(text="O", fg="White", activeforeground="white")
        # print(AI)
        if AI:
            x = random.randint(0, 10)
            y = random.randint(0, 10)
            ai_shoots(y, x, board2, ai_score)
    if player_1_hits == 20 or player_2_hits == 20:
        popupwindow(player + " has won!")


def side(player, allbuttons):
    print(player)
    if player == "player 1":
        for row in range(10):
            for column in range(10):
                allbuttons[row][column].grid(row=1 + row, column=4 + column)

        label2 = Label(root, text="Player 1", font=font1, fg="white", bg="gray19")
        label2.grid(row=11, column=4, columnspan=10)
    else:
        for row in range(10):
            for column in range(10):
                allbuttons[row][column].grid(row=1 + row, column=15 + column)

        label3 = Label(root, textvariable=player2_or_AI, font=font1, fg="white", bg="gray19")
        label3.grid(row=11, column=15, columnspan=10)


def board_buttons(board, info, player, player_1_hits, player_2_hits, ai_score, board2):
    allbuttons = []
    a = 0
    print(AI)
    for i in range(10):
        b = 0
        buttons = []
        for j in range(10):
            button = Button(root, width=2, height=1, font=font1, bg="sky blue", activebackground="sky blue",
                            command=partial(hit_or_miss, a, b, board, allbuttons,
                                            info, player, player_1_hits, player_2_hits, ai_score, board2))
            buttons.append(button)
            b += 1
        allbuttons.append(list(buttons))
        a += 1
    every_button.append(allbuttons)
    side(player, allbuttons)


def middle_board_space():
    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=1 + _, column=14)


def main():
    player_1_hits = 0
    player_2_hits = 0
    ai_hits = 0

    player_1_board = player_board()
    player_2_board = player_board()

    place_all_ships(player_1_board)
    place_all_ships(player_2_board)

    info = StringVar()
    side_labels()

    board_buttons(player_1_board, info, "player 1", player_1_hits, player_2_hits, ai_hits, player_2_board)
    middle_board_space()
    board_buttons(player_2_board, info, "player 2", player_1_hits, player_2_hits, ai_hits, player_1_board)


main()
root.mainloop()
Reply
#3
If you going to do a GUI you definitely need classes. For games, classes are also very good. For my Battleships game, I have a class for the players and one for the boards. There are some basic class tutorials on the board, see the link in my signature below.
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#4
@ThiefOfTimes Hi, I've been trying to implement the tips you gave me but to no avail. How would you do it?
Reply
#5
This is how I would do it, using your code. Although the game can not be ended by winning right now.
Wrapping your code into classes would mean to refactor your complete code. I'm sorry that I am not doing this now.
Grab the books (contained in the links I send) and explore classes and objects. Then I would pack all players (AI as well) in one class and the boards into one class. You can also create a class for the battle ships since they share some attributes. I would create the player_boards in the __init__ method of the board class and save them in variables/attributes. Also create the player objects there. Then take a look which methods fit in which class.
Maybe start creating the player class since it would be the smallest :)

from Tkinter import *
from tkFont import Font
import tkMessageBox
from functools import partial
import random
import time
import sys
import os

root = Tk()
root.wm_title("BATTLESHIPS")
root.configure(background='gray19')
font1 = Font(family='Helvetica', size=12, weight='bold')
font_big = Font(family='Helvetica', size=16, weight='bold')
font_normal = Font(family='Helvetica', size=10, weight='normal')

ships = {"Aircraft Carrier": 4, "Battleship": 3, "Submarine": 2, "Destroyer": 1}
AI = False


def restart_program():
    '''
    This method restarts the game script
    '''
    python = sys.executable
    os.execl(python, python, *sys.argv)


def player_board():
    """
    generating a 2 dimensional array representing one players board

    :return: two dimensional list
    """
    board = []
    t = []
    # creating the upper bounder
    t += (10 + 2) * ['# ']
    board.append(t)

    # creating one line in the board
    rad = ['# ']
    for r in range(0, 10):
        rad.append("~ ")
    # inserting the new line into the board
    rad.append('# ')
    for k in range(0, 10):
        board.append(list(rad))
    # inserting the lower bounder
    board.append(t)
    return board


def place_ship(ship, board):
    """
    place ship onto given board

    :param ship: given ships name
    :param board: board of given player
    """
    # w = 0
    while True:
        checkcoords = []
        x = random.randint(1, 10)
        y = random.randint(1, 10)
        o = random.randint(0, 1)
        if o == 0:
            ori = "v"  # vertical
        else:
            ori = "h"  # horizontal
        # if ship would be placed outside of the board skip
        if (ori == "v" and y + ships[ship] > 10) or (ori == "h" and x + ships[ship] > 10):
            pass
            # w = 0
        else:
            if ori == "v":
                # vertical placement
                # if no ship is near this position place it
                for i in range(-1, (ships[ship] + 1)):
                    for j in range(-1, 2):
                        checkcoords.append(board[y + i][x + j])
                if ': ' not in checkcoords:
                    for i in range(ships[ship]):
                        board[y + i][x] = ': '
                    break
            #                else:
            #                    w = 0
            elif ori == "h":
                # horizontal placement
                # if no ship is near this position place it
                for i in range(-1, (ships[ship] + 1)):
                    for j in range(-1, 2):
                        checkcoords.append(board[y + j][x + i])
                if ': ' not in checkcoords:
                    for i in range(ships[ship]):
                        board[y][x + i] = ': '
                    break


#                else:
#                    w = 0


def place_all_ships(board):
    """
    Place all ships on the given board

    :param board: given player board
    """
    for ship in ships:
        for _ in range(0, (5 - ships[ship])):
            place_ship(ship, board)


def popupwindow(msg):
    """
    Pop up window if game is over

    :param msg: player name
    """
    answer = tkMessageBox.askquestion("Game Over", msg + " Would you like to play again?")
    if answer == "yes":
        restart_program()
    elif answer == "no":
        quit()


def nr_players(number):
    """
    Set number of players

    :param number: number of needed players
    """
    global AI
    # activate player2 Buttons
    for bt_list in every_button[1]:
        for bt in bt_list:
            bt['state'] = 'normal'
    # if one player is needed activate AI
    if number == 1:
        player2_or_AI.set("AI")
        AI = True
    else:
        # activate player2 Buttons
        for bt_list in every_button[0]:
            for bt in bt_list:
                bt['state'] = 'normal'
        player2_or_AI.set("Player 2")


info = StringVar()
player2_or_AI = StringVar()
every_button = []


def side_labels():
    """
    Create Buttons and Labels for the field
    """
    # info = StringVar()
    Label(root, text="BATTLESHIPS", fg="white", bg="gray19", font=font_big).grid(row=0, column=10, columnspan=9)
    Label(root, textvariable=info, fg="white", bg="gray19", font=font1).grid(row=12, column=6, columnspan=18)

    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=_, column=0)
    Button(root, width=7, height=1, text="1 Player", font=font1, fg="white", activebackground="gray19",
           bg="gray19", command=lambda: nr_players(1)).grid(row=2, column=1)
    Button(root, width=7, height=1, text="2 Players", font=font1, fg="white", activebackground="gray19",
           bg="gray19", command=lambda: nr_players(2)).grid(row=3, column=1)
    Label(root, text="Get 20 hits to win", font=font_normal, fg="white", bg="gray19").grid(row=5, column=1)
    Label(root, text="1 Battleship 4 units", font=font_normal, fg="white", bg="gray19").grid(row=6, column=1)
    Label(root, text="2 Battleships 3 units", font=font_normal, fg="white", bg="gray19").grid(row=7, column=1)
    Label(root, text="3 Battleships 2 units", font=font_normal, fg="white", bg="gray19").grid(row=8, column=1)
    Label(root, text="4 Battleships 1 unit  ", font=font_normal, fg="white", bg="gray19").grid(row=9, column=1)

    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=_, column=2)

    for _ in range(10):
        Label(root, width=20, text="   ", bg="gray19").grid(row=_, column=25)


def ai_shoots(y_coord, x_coord, player_1_board, ai_score):
    """
    AI shooting method

    :param y_coord: y coordinate to shoot at
    :param x_coord: x coordinate to shoot at
    :param player_1_board: board to shoot at
    :param ai_score: score of AI
    """
    # if score is 20, AI has won
    if ai_score == 20:
        popupwindow("The computer has won.")
    # if AI got one hit, destroy complete ship
    if player_1_board[y_coord][x_coord] == ': ':
        ai_score += 1
        player_1_board[y_coord][x_coord] = 'X '
        every_button[0][y_coord - 1][x_coord - 1].configure(text="X", fg="black", bg="red3")
        # depending of where the rest of the ship is located, shot it
        if player_1_board[y_coord - 1][x_coord] == ': ':
            ai_shoots(y_coord - 1, x_coord, player_1_board, ai_score)
        elif player_1_board[y_coord + 1][x_coord] == ': ':
            ai_shoots(y_coord + 1, x_coord, player_1_board, ai_score)
        elif player_1_board[y_coord][x_coord - 1] == ': ':
            ai_shoots(y_coord, x_coord - 1, player_1_board, ai_score)
        elif player_1_board[y_coord][x_coord + 1] == ': ':
            ai_shoots(y_coord, x_coord + 1, player_1_board, ai_score)
        else:
            # shot some random position if ship is destroyed
            x = random.randint(1, 10)
            y = random.randint(1, 10)
            ai_shoots(y, x, player_1_board, ai_score)
    elif player_1_board[y_coord][x_coord] == 'X ' or player_1_board[y_coord][x_coord] == 'O ':
        # if position was already shoot at, try a new position
        x = random.randint(1, 10)
        y = random.randint(1, 10)
        ai_shoots(y, x, player_1_board, ai_score)
    else:
        # if water was hit just change the button
        player_1_board[y_coord][x_coord] = 'O '
        every_button[0][y_coord - 1][x_coord - 1].configure(text="O", fg="white")


def hit_or_miss(a, b, board, all_buttons, info, player, player_1_hits, player_2_hits, ai_score, board2):
    """
    check if there was a hit or a missed done by the player

    :param a: clicked y position
    :param b: clicked x position
    :param board: opponents player board
    :param all_buttons: all buttons of the opponent board
    :param info: write some info to the screen
    :param player: player name
    :param player_1_hits: number of player_1_hits
    :param player_2_hits: number of player_2_hits
    :param ai_score: number of AI hits
    :param board2: own players board
    """
    global AI
    # if a already hit place was clicked again write message
    if board[a + 1][b + 1] == 'O ' or board[a + 1][b + 1] == 'X ':
        info.set("You have already fired there, " + player + "!")
    elif board[a + 1][b + 1] == ': ':
        # if a ship was hit change the button and the players board
        info.set("A hit, nice shot " + player + "!")
        board[a + 1][b + 1] = 'X '
        all_buttons[a][b].configure(text="X", fg="black", bg="red3", activebackground="red3")
        # increase the hit counter and go again
        if player == "player 1":
            player_1_hits += 1
        else:
            player_2_hits += 1
    else:
        # in case of a miss change the button and trigger the AI
        info.set("Seems like you missed that one, " + player + "!")
        board[a + 1][b + 1] = 'O '
        all_buttons[a][b].configure(text="O", fg="White", activeforeground="white")
        # print(AI)
        if AI:
            x = random.randint(0, 10)
            y = random.randint(0, 10)
            ai_shoots(y, x, board2, ai_score)
    if player_1_hits == 20 or player_2_hits == 20:
        # if one player got 20 hits he won
        popupwindow(player + " has won!")


def side(player, allbuttons):
    """
    order the buttons of each player into a grid

    :param player: players name
    :param allbuttons: all buttons of this player
    """
    print(player)
    col = 4 if player == "player 1" else 15
    for row in range(10):
        for column in range(10):
            allbuttons[row][column].grid(row=1 + row, column=col + column)
    if player == "player 1":
        label2 = Label(root, text="Player 1", font=font1, fg="white", bg="gray19")
        label2.grid(row=11, column=4, columnspan=10)
    else:
        label3 = Label(root, textvariable=player2_or_AI, font=font1, fg="white", bg="gray19")
        label3.grid(row=11, column=15, columnspan=10)


def board_buttons(board, info, player, player_1_hits, player_2_hits, ai_score, board2):
    """
    create all buttons for one player

    :param board: players board
    :param info: writing info to the screen
    :param player: players name
    :param player_1_hits: number of player 1 hits
    :param player_2_hits: number of player 2 hits
    :param ai_score: AI score
    :param board2: opponents board
    """
    allbuttons = []
    a = 0
    print(AI)
    for i in range(10):
        b = 0
        buttons = []
        for j in range(10):
            button = Button(root, width=2, height=1, font=font1, bg="sky blue", activebackground="sky blue",
                            command=partial(hit_or_miss, a, b, board, allbuttons,
                                            info, player, player_1_hits, player_2_hits, ai_score, board2), state="disable")
            buttons.append(button)
            b += 1
        # create a 2 dimensional array with buttons representing the battle field
        allbuttons.append(list(buttons))
        a += 1
    # store each button matrix in a list for later and global access
    every_button.append(allbuttons)
    side(player, allbuttons)


def middle_board_space():
    """
    Insert the middel space
    """
    for _ in range(10):
        Label(root, text="   ", bg="gray19").grid(row=1 + _, column=14)


def main():
    """
    primary game method
    """
    player_1_hits = 0
    player_2_hits = 0
    ai_hits = 0

    # initialize the player boards
    player_1_board = player_board()
    player_2_board = player_board()

    # place all ships on the boards
    place_all_ships(player_1_board)
    place_all_ships(player_2_board)

    # instert side labels
    info = StringVar()
    side_labels()

    # create the buttons
    board_buttons(player_1_board, info, "player 1", player_1_hits, player_2_hits, ai_hits, player_2_board)
    middle_board_space()
    board_buttons(player_2_board, info, "player 2", player_1_hits, player_2_hits, ai_hits, player_1_board)


main()
root.mainloop()
Reply
#6
@ThiefOfTimes Hi,sorry for asking you over and over. I have read about classes in the books you linked and I think I have a fair understanding now, but I can't figure out how to "translate" my specific code into classes. I've been going at this for days now and I make little to no progress.

Could you take a bit of my code and make it into a class (for example the board or something) so I can see and example? With that I could probably figure the rest out.

Thanks a bunch.
Reply
#7
Yeah sure, no problem mate :)
I think the players are great examples.
Player:
class Player:
     def __init__(self, board, ships):
          self.board = board
          self.op_board = list(board)
          self.ships = ships
          self.counter = 0

     sef inc_counter(self):
          self.counter += 1

     def place_ships(self):
         """
         Place all ships on the given board
         """
         for ship in self.ships:
             for _ in range(0, (5 - ships[ship])):
                 while True:
                      checkcoords = []
                      x = random.randint(1, 10)
                      y = random.randint(1, 10)
                      o = random.randint(0, 1)
                      if o == 0:
                           ori = "v"  # vertical
                      else:
                           ori = "h"  # horizontal
                      # if ship would be placed outside of the board skip
                      if (ori == "v" and y + self.ships[ship] > 10) or (ori == "h" and x + self.ships[ship] > 10):
                           pass
                      else:
                           if ori == "v":
                                # vertical placement
                                # if no ship is near this position place it
                                for i in range(-1, (self.ships[ship] + 1)):
                                     for j in range(-1, 2):
                                          checkcoords.append(board[y + i][x + j])
                                if ': ' not in checkcoords:
                                     for i in range(self.ships[ship]):
                                          self.board[y + i][x] = ': '
                                     break
                           elif ori == "h":
                                # horizontal placement
                                # if no ship is near this position place it
                                for i in range(-1, (self.ships[ship] + 1)):
                                     for j in range(-1, 2):
                                          checkcoords.append(self.board[y + j][x + i])
                                if ': ' not in checkcoords:
                                     for i in range(self.ships[ship]):
                                            self.board[y][x + i] = ': '
                                     break
     def get_board(self):
          return self.board

     def hit_or_miss(self, a, b, all_buttons, info):
          """
          check if there was a hit or a missed done by the player
          :param a: clicked y position          
          :param b: clicked x position
          :param all_buttons: all buttons of the opponent board
          :param info: write some info to the screen
          """
          global AI
          # if a already hit place was clicked again write message
          if self.board[a + 1][b + 1] == 'O ' or self.board[a + 1][b + 1] == 'X ':
               info.set("You have already fired there, " + player + "!")
               return 0
          elif self.board[a + 1][b + 1] == ': ':
               # if a ship was hit change the button and the players board
               info.set("A hit, nice shot " + player + "!")
               all_buttons[a][b].configure(text="X", fg="black", bg="red3", activebackground="red3")
               return 1
          else:
               # in case of a miss change the button and trigger the AI
               info.set("Seems like you missed that one, " + player + "!")
               all_buttons[a][b].configure(text="O", fg="White", activeforeground="white")
               return 2

class AI(Player):
     def __init__(self, opponent, board, ships):
          super(AI,self).__init__(board,ships)
          self.opponent = opponent

     def shoot(self, every_button, info, x_s=None, y_s=None):
          if x_s is None or y_s is None:
               x = random.randint(0, 10)
               y = random.randint(0, 10)
          else:
               x = x_s
               y = y_s
          # if AI got one hit, destroy complete ship
          if self.opponent.hit_or_miss(y, x, every_button, info) == 1:
               self.inc_score()
               every_button[0][y- 1][x - 1].configure(text="X", fg="black", bg="red3")
               # depending of where the rest of the ship is located, shot it
               if self.opponent.getBoard()[y - 1][x] == ': ':
                    ai_shoots(every_button, info, y - 1, x)
               elif self.opponent.getBoard()[y + 1][x] == ': ':
                    ai_shoots(every_button, info, y + 1, x)
               elif player_1_board[y_coord][x_coord - 1] == ': ':
                    ai_shoots(every_button, info, y, x - 1)
               elif player_1_board[y_coord][x_coord + 1] == ': ':
                    ai_shoots(every_button, info, y, x + 1)
               else:
                    # shot some random position if ship is destroyed
                    x = random.randint(1, 10)
                    y = random.randint(1, 10)
                    ai_shoots(every_button, info, y, x)
          elif self.opponent.hit_or_miss(y, x, every_button, info) == 0:
               # if position was already shoot at, try a new position
               x = random.randint(1, 10)
               y = random.randint(1, 10)
               ai_shoots(every_button, info, y, x)
          else:
               # if water was hit just change the button
               every_button[0][y - 1][x - 1].configure(text="O", fg="white")
I hope I haven't forgot anything, the problem is, that I can't test it right now. But this is an example of how the players could be implemented as classes accourding to your code. Note that the way you used the methods before has changed a little bit and there still can be made adjustments.
Hope this example helps :) The next step would be the board.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020