简体   繁体   中英

How to fix my Python 3.6 battleship code?

Basically when I click left mousebutton and coordinates of a cell matches I want to change parameter self.shot to True forever (it checks if a cell has been shot before and if a cell is shot it draws either a dot or a sharp). The problem is that this parameter only changes when mousebutton is pressed and changes back to its standard value when button is up. At this point I don't really know how to rebuild this code to work well.

Here's the code:

import pygame, random, time, math

pygame.init()

clock = pygame.time.Clock()

yours = 0
enemys = 1
exitPossible = False
gameStart = False
tour = random.randint(yours,enemys)

white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)

one = 32
two = 63
three = 94
four = 125
five = 156
six = 187
seven = 218
eight = 249
nine = 280
ten = 301

a = 32
b = 63
c = 94
d = 125
e = 156
f = 187
g = 218
h = 249
i = 280
j = 311

#lista

m = 0
n = 0
o = 0
p = 0
playerWins = 10
botWins = 0


window = pygame.display.set_mode((760, 410))
pygame.display.set_caption("Battleship")
font = pygame.font.SysFont("Arial", 30)

class crate:

    def __init__(self, sizex, sizey, shot, shiphere):

        self.sizex, self.sizey = sizex, sizey
        self.shot = shot
        self.shiphere = shiphere




    def crateShot(self):

        if not exitPossible:
            if not self.shot:
                if event.type == pygame.MOUSEBUTTONDOWN and self.sizex + 30 > mx > self.sizex and self.sizey + 30 > my > self.sizey:
                    self.shot = True


            if self.shot:
                if self.shiphere:
                    pygame.draw.line(window, white, (self.sizex, self.sizey), (self.sizex + 30, self.sizey + 30), 3)
                    pygame.draw.line(window, white, (self.sizex + 29, self.sizey), (self.sizex, self.sizey + 29), 3)
                else:
                    pygame.draw.circle(window, white, (self.sizex + 15, self.sizey + 15), 6, 5)




allCrates = ()

def crates(allCrates):
    a1 = crate(a, a, True, False)
    a1.crateShot()


    a2 = crate(b, a, True, True)
    a2.crateShot()


    a3 = crate(c, a, False, False)
    a3.crateShot()


    a4 = crate(d, a, False, True)
    a4.crateShot()



#slownik how to
ololollololol = {
    "" : 123
}

class ship:
    def __init__(self, size, amount):
        self.size = size
        self.amount = amount

    #def drawShip(self):


def ships():
    carrier = ship(4, 1)
    battleship = ship(3, 2)
    cruiser = ship(2, 3)
    destroyer = ship(1, 4)


def playerWindow(m, n):
    if not exitPossible:
        window.fill(black)
        for i in range(11):
            pygame.draw.line(window, white, (31 + m, 0), (31 + m, 341))
            m += 31
        for i in range(11):
            pygame.draw.line(window, white, (0, 31 + n), (341, 31 + n))
            n += 31


        a = font.render("A", True, white)
        window.blit(a, (4, 30))

        b = font.render("B", True, white)
        window.blit(b, (5, 61))

        c = font.render("C", True, white)
        window.blit(c, (4, 92))

        d = font.render("D", True, white)
        window.blit(d, (4, 123))

        e = font.render("E", True, white)
        window.blit(e, (5, 154))

        f = font.render("F", True, white)
        window.blit(f, (6, 185))

        g = font.render("G", False, white)
        window.blit(g, (3, 216))

        h = font.render("H", True, white)
        window.blit(h, (4, 247))

        i = font.render("I", True, white)
        window.blit(i, (12, 278))

        j = font.render("J", True, white)
        window.blit(j, (7, 309))

        a = font.render("1", True, white)
        window.blit(a, (39, -2))

        b = font.render("2", True, white)
        window.blit(b, (70, -2))

        c = font.render("3", True, white)
        window.blit(c, (101, -2))

        d = font.render("4", True, white)
        window.blit(d, (132, -2))

        e = font.render("5", True, white)
        window.blit(e, (163, -2))

        f = font.render("6", True, white)
        window.blit(f, (194, -2))

        g = font.render("7", True, white)
        window.blit(g, (225, -2))

        h = font.render("8", True, white)
        window.blit(h, (256, -2))

        i = font.render("9", True, white)
        window.blit(i, (287, -2))

        j = font.render("10", True, white)
        window.blit(j, (308, -2))


def botWindow(o, p):
    if not exitPossible:
        for i in range(11):
            pygame.draw.line(window, white, (419 + o, 0), (419 + o, 341))
            o += 31
        for i in range(11):
            pygame.draw.line(window, white, (419, 31 + p), (760, 31 + p))
            p += 31


        a = font.render("A", True, white)
        window.blit(a, (425, 30))

        b = font.render("B", True, white)
        window.blit(b, (425, 61))

        c = font.render("C", True, white)
        window.blit(c, (424, 92))

        d = font.render("D", True, white)
        window.blit(d, (424, 123))

        e = font.render("E", True, white)
        window.blit(e, (425, 154))

        f = font.render("F", True, white)
        window.blit(f, (426, 185))

        g = font.render("G", True, white)
        window.blit(g, (423, 216))

        h = font.render("H", True, white)
        window.blit(h, (424, 247))

        i = font.render("I", True, white)
        window.blit(i, (432, 278))

        j = font.render("J", True, white)
        window.blit(j, (427, 309))

        a = font.render("1", True, white)
        window.blit(a, (458, -2))

        b = font.render("2", True, white)
        window.blit(b, (489, -2))

        c = font.render("3", True, white)
        window.blit(c, (520, -2))

        d = font.render("4", True, white)
        window.blit(d, (551, -2))

        e = font.render("5", True, white)
        window.blit(e, (582, -2))

        f = font.render("6", True, white)
        window.blit(f, (613, -2))

        g = font.render("7", True, white)
        window.blit(g, (644, -2))

        h = font.render("8", True, white)
        window.blit(h, (675, -2))

        i = font.render("9", True, white)
        window.blit(i, (706, -2))

        j = font.render("10", True, white)
        window.blit(j, (727, -2))


def gameWindow(playerWins, botWins):
    if not exitPossible:
        score = font.render("SCORE:", True, white)
        window.blit(score, (325, 345))
        wins = font.render(str(playerWins) + " - " + str(botWins), True, white)
        window.blit(wins, (325, 375))
        auto = font.render("AUTO", True, white)
        window.blit(auto, (13, 359))
        pygame.draw.rect(window, white, (10, 361, 89, 30), 1)


while True:
    mx, my = pygame.mouse.get_pos()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()



        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            exitPossible = True

    if exitPossible:
        window.fill(black)
        uSure = font.render("ARE YOU SURE YOU WANT TO EXIT?", True, white)
        window.blit(uSure, (115, 145))
        YES = font.render("YES", True, white)
        window.blit(YES, (335, 195))
        NO = font.render("NO", True, white)
        window.blit(NO, (342, 230))

        if event.type == pygame.MOUSEBUTTONDOWN:
            if 390 >= mx >= 335 and 223 >= my >= 200:
                quit()
            elif 387 >= mx >= 342 and 256 >= my >= 235:
                exitPossible = False
                window.fill(black)


    playerWindow(m, n)
    botWindow(o, p)
    gameWindow(playerWins, botWins)
    crates(allCrates)
    pygame.display.flip()
    clock.tick(60)

Currently the code needs a new feature:

Detect a mouse click, determine which cell "crate" it happened in (if any)

However all the existing code uses kind of a "brute force" approach using lots of single variables for locations. This makes it hard to check all the positions without doing it 100 times, once for each crate.

I think forward-progress on the code would be easier if it were changed a little to use lists of rectangles for the player's grid. Please consider the code:

# Create the click-grid for the player
GRID_SIZE   = 31
player_grid = []
for y_cell in [ a,b,c,d,e,f,g,h,i,j ]:
    for x_cell in [ one, two, three, four, five, six, seven, eight, nine, ten ]:
        player_grid.append( [ x_cell, y_cell, GRID_SIZE, GRID_SIZE ] )

What we have after this operation is a python list of lists, so player_grid[0] is [ one, a, 31, 31 ] , followed by [ two, a, 31, 31 ] , etc. All the elements in the player_grid list are just records of position and size, it's not anything complex. The reason to go to all this trouble making the list is because it allows the code to simply loop over all the rectangles inside checking them.

In the example, I have used the code's existing a , b , c , ... variables because they are already defined in the question's code. But this could easily have been range( 0, 10 ) and some factor of window-size.

So now in player_grid we have 100 rectangles. What would be good, is if we could make a crate object in the same way, having the crate "know" its position. This allows the crate object (given an x,y) to tell us if it was clicked. So, lets do some minor modifications to crate - change the __init__() to take an x and y , and store a PyGame Rect as a member variable:

class crate:
    def __init__(self, x, y, sizex, sizey, shot, shiphere):
        self.x        = x
        self.y        = y
        self.sizex    = sizex
        self.sizey    = sizey
        self.rect     = pygame.Rect( x, y, sizex, sizey )
        self.shot     = shot
        self.shiphere = shiphere

And now, make a list of crate in a loop similar to before:

# Create the click-grid for the player
GRID_SIZE   = 31
all_crates  = []
for y_cell in [ a,b,c,d,e,f,g,h,i,j ]:
    for x_cell in [ one, two, three, four, five, six, seven, eight, nine, ten ]:
        all_crates.append( crate( x_cell, y_cell, GRID_SIZE, GRID_SIZE, False, False ) )

What we have after this operation is a python list of crate objects, each of which contains a PyGame Rect . A Rect in this sense is just a position and size, it's not anything too complex. But it does allow for some easier checking as there's a bunch of handy collision functions in a PyGame Rect.

Back to the problem - the user has clicked on the screen, and we need to see if it's in one of the 100 crate s. Now that each crate knows it's rectangular position, it's easy to loop through them all, checking in turn:

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        elif event.type == pygame.MOUSEBUTTONUP:  # User released mouse button
            mx, my = pygame.mouse.get_pos()       # Position of the click

            # Loop through all crate objects, seeing if any where clicked
            for c in all_crates:
                if c.rect.collidepoint( mx, my ) == True:
                    # User clicked inside a crate
                    c.crateShot( mx, my )         # Test for hit
                    break                         # No further checks needed

Personally, I would create a member function in crate , and used this instead of directly referencing the crate.rect from outside the object.

def wasClicked( self, mouse_x, mouse_y ):
    return self.rect.collidepoint( mouse_x, mouse_y )

While trivial in this case, it allows any future extra processing and checking to be added into the crate object, rather than "polluting" the simplicity of the main-loop.

Note: I've tried to make as few code changes a possible, keeping the style and existing code. However I think it would be a good learning exercise to re-write the screen grid logic to use a factor of window size and construct the grid dynamically, rather than lists of hand-tuned co-ordinates. Despite it being more effort initially, things like drawing the lists of grid-letter headings could become a simple 5-line loop.

For example, something like:

WINDOW_WIDTH  = 1000
WINDOW_HEIGHT = 500
CELL_COUNT    = 10
GRID_SIZE     = WINDOW_HEIGHT // (CELL_COUNT * 0.7)  # cells cover 70% of the window-height
GRID_OFFSET   = GRID_SIZE * 2                        # Leave room for headings, etc.

# Define the player grid
player_grid = []
for y in range( 0, CELL_COUNT ):
    for x in range( 0, CELL_COUNT ):
        pos_x = x + GRID_OFFSET  # move the grid away from the edge
        pos_y = y + GRID_OFFSET 
        player_grid.append( pygame.Rect( pos_x, pos_y, GRID_SIZE, GRID_SIZE ) )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM