Advertisement
maincarry

Battleship! Pygame Code

Oct 29th, 2016
825
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 44.29 KB | None | 0 0
  1. '''
  2. Battleship! Game
  3. Licensed under CC BY-NC-SA 4.0.
  4. https://creativecommons.org/licenses/by-nc-sa/4.0/
  5.  
  6. By Mark Wu
  7. @markwu.me
  8. '''
  9.  
  10. import pygame,sys,copy,random
  11. from pygame.locals import *
  12.  
  13. class Struct: pass
  14. # ---------------- MODULE LEVEL DEFINITION ----------------
  15. gameData = Struct()
  16. colors = Struct()
  17. FPSCLOCK = pygame.time.Clock()
  18. DISPLAYSURF = pygame.display.set_mode((800, 600))
  19.  
  20. # FOR DEBUG USE
  21. VIEW_ALL = False
  22.  
  23. # THIS SHOULD BE SET TO TRUE
  24. VIEW_COMPUTER_OVERLAY = True
  25. # ---------------- FUNCTIONS ----------------
  26.  
  27.  
  28. def makeTextObjs(text, font, color):
  29.     '''
  30.    a shortcut which returns two obj: textSurfaceObj, textRectObj
  31.  
  32.    :return: textSurfaceObj, textRectObj
  33.    '''
  34.     surfObj = font.render(text, True, color)
  35.     return surfObj, surfObj.get_rect()
  36.  
  37.  
  38. def terminate():
  39.     '''
  40.    a shortcut to end the program
  41.    '''
  42.     pygame.quit()
  43.     sys.exit()
  44.  
  45.  
  46. def runStartScreen():
  47.     DISPLAYSURF.fill(colors.WHITE)
  48.     helpBG = pygame.image.load('data/help.fw.png')
  49.     bg = pygame.image.load('data/start_BG.fw.png')
  50.     while True:
  51.         if gameData.showHelp:
  52.             DISPLAYSURF.blit(helpBG, (0, 0))
  53.         else:
  54.             DISPLAYSURF.blit(bg, (0, 0))
  55.         for event in pygame.event.get():
  56.             if event.type == QUIT: terminate()
  57.             if event.type == MOUSEBUTTONUP:
  58.                 gameData.showHelp = True
  59.             if event.type == KEYDOWN:
  60.                 # end the Start Screen
  61.                 gameData.showHelp = False
  62.                 return
  63.         pygame.display.update()
  64.         FPSCLOCK.tick(gameData.FPS)
  65.  
  66.  
  67. def drawCell(tl_pos, size, color):
  68.     '''
  69.    this function draws individual cells with top_left corner(pos)
  70.    '''
  71.     pygame.draw.rect(DISPLAYSURF, color, (tl_pos[0], tl_pos[1], size, size))
  72.  
  73.  
  74. def getCellColor(board, row, col):
  75.     '''
  76.    This returns the color needed to be drawed at that cell
  77.    '''
  78.     cell_info = board[row][col]
  79.     if cell_info == '':
  80.         return colors.EMPTY_CELL
  81.     else:
  82.         cell_data = readCellContent(board, row, col)
  83.         # [player,shipName,orientation,posIndex, status]
  84.         if cell_data[4] == gameData.STATUS_HIT:
  85.             return colors.BLACK
  86.         if cell_data[0] == 'p1':
  87.             return colors.P1_SHIP_BG
  88.         if cell_data[0] == 'p2':
  89.             return colors.P2_SHIP_BG
  90.  
  91.     return colors.WHITE
  92.  
  93.  
  94.  
  95.  
  96. def drawBoarderLine(tl_pos, cell_size):
  97.     right_x = tl_pos[0]+gameData.col*cell_size
  98.     bottom_y = tl_pos[1]+gameData.row*cell_size
  99.     for row in xrange(1, gameData.row):
  100.         pygame.draw.aaline(DISPLAYSURF, colors.CELL_BOARDERLINE,
  101.                 (tl_pos[0], tl_pos[1] + row * cell_size),
  102.                 (right_x, tl_pos[1] + row * cell_size))
  103.     for col in xrange(1, gameData.col):
  104.         pygame.draw.aaline(DISPLAYSURF, colors.CELL_BOARDERLINE,
  105.                 (tl_pos[0]+col*cell_size, tl_pos[1]),
  106.                 (tl_pos[0]+col*cell_size, bottom_y))
  107.  
  108.     # draw outer boarder
  109.     inner_margin = 8
  110.     boarderRect = [tl_pos[0]-inner_margin,tl_pos[1]-inner_margin,
  111.                    gameData.col * gameData.boardUI_cellSize+inner_margin,
  112.                    gameData.row * gameData.boardUI_cellSize+inner_margin]
  113.     pygame.draw.rect(DISPLAYSURF, colors.BLACK, boarderRect, 8)
  114.  
  115.  
  116. def drawBoard(board, tl_pos):
  117.     '''
  118.    This function draws the board with top_left corner(pos)
  119.    '''
  120.     cell_size = gameData.boardUI_cellSize
  121.     # draw indiv cell
  122.     for row in xrange(gameData.row):
  123.         for col in xrange(gameData.col):
  124.             # draw cell
  125.             cellColor = getCellColor(board, row, col)
  126.             cell_pos = (tl_pos[0] + col * cell_size,
  127.                         tl_pos[1] + row * cell_size)
  128.             drawCell(cell_pos, cell_size, cellColor)
  129.     # draw boarder line
  130.     drawBoarderLine(tl_pos, cell_size)
  131.  
  132.  
  133. def drawEnergyBar():
  134.     # update txt
  135.     prompt = 'Your Energy: ' if gameData.currentPlayer == 'p1'else\
  136.         "Enemy's Turn: "
  137.     gameData.txt_EB = prompt + ('%s / %s' %
  138.                                 (gameData.currentEnergy[gameData.currentPlayer],
  139.                                  gameData.maxEnergy[gameData.currentPlayer]))
  140.  
  141.     BG_color = colors.EB_BG if gameData.currentPlayer == 'p1'else\
  142.         colors.EB_BG2
  143.     # draw boarder
  144.     pygame.draw.rect(DISPLAYSURF, BG_color, gameData.EB_Pos)
  145.     pygame.draw.rect(DISPLAYSURF, colors.EB_BOARDER,gameData.EB_Pos,4)
  146.     # draw text
  147.     txt_EB, txt_EBRect = \
  148.         makeTextObjs(gameData.txt_EB, gameData.BASICFONT, colors.BLACK)
  149.     txt_EBRect.center = pygame.Rect(gameData.EB_Pos).center
  150.     DISPLAYSURF.blit(txt_EB, txt_EBRect)
  151.  
  152.  
  153.  
  154. def getSkillName(skills_list, i):
  155.     '''
  156.    return the skills name at index i of the skills_list
  157.    '''
  158.     if skills_list[i] == '':
  159.         return gameData.blankSkillName
  160.     else:
  161.         return skills_list[i]
  162.  
  163.  
  164. def drawSkillsPanal(skills_list):
  165.     # draw bg
  166.     pygame.draw.rect(DISPLAYSURF, colors.UI_BG, gameData.Skills_Pos)
  167.     pygame.draw.rect(DISPLAYSURF, colors.BLACK,gameData.Skills_Pos,1)
  168.  
  169.     # draw central split line
  170.     pygame.draw.aaline(DISPLAYSURF, colors.BLACK,
  171.                      (gameData.Skills_Pos[0]+24, gameData.Skills_Pos[1]+78),
  172.                      (gameData.Skills_Pos[0]+301, gameData.Skills_Pos[1]+78),
  173.                      2)
  174.     # draw Skill Boxs
  175.     for i in range(len(gameData.skillBox)):
  176.         box_rect = gameData.skillBox[i]
  177.         pygame.draw.rect(DISPLAYSURF, colors.WHITE, box_rect)
  178.         pygame.draw.rect(DISPLAYSURF, colors.BLACK, box_rect,1)
  179.         # fill skill icon
  180.         skill_name = getSkillName(skills_list,i)
  181.         DISPLAYSURF.blit(gameData.IMAGESDICT[skill_name],
  182.                          (box_rect[0],box_rect[1]))
  183.     # draw text
  184.     tl = gameData.Skills_txt_Pos
  185.     margin = 25
  186.     line = 1
  187.     for txt in gameData.txt_skillDescription:
  188.         txt_skillDescription, txt_skillDescriptionRect = \
  189.             makeTextObjs(txt, gameData.BASICFONT,
  190.                          colors.BLACK)
  191.         txt_skillDescriptionRect.center = (tl[0], tl[1]+margin*line)
  192.         DISPLAYSURF.blit(txt_skillDescription, txt_skillDescriptionRect)
  193.         line += 1
  194.  
  195.  
  196. def drawShipImages(player):
  197.     '''
  198.    cover the ship with images only for p1
  199.    '''
  200.  
  201.     # eg. {'submarine': [(12, 9), (13, 9)],
  202.     # 'battleship': [(7, 3), (8, 3), (9, 3), (10, 3)], }
  203.     for ship_name in gameData.shipImagePos[player]:
  204.         ship_data = gameData.shipImagePos[player][ship_name]
  205.         imageRect = convertBoardToPixel(*ship_data[0])
  206.  
  207.         DISPLAYSURF.blit(gameData.IMAGESDICT[ship_name],
  208.                          imageRect)
  209.  
  210.  
  211. def drawUIOverlay(player):
  212.     '''
  213.    print relevent UI Overlay features
  214.    '''
  215.     tl_pos = gameData.boardUI_TL_Pos
  216.     cell_size = gameData.boardUI_cellSize
  217.  
  218.     # draw indiv cell
  219.     for row in xrange(gameData.row):
  220.         for col in xrange(gameData.col):
  221.             # draw cell
  222.             content = gameData.UIOverLay[player][row][col]
  223.             if content == '': continue
  224.             if content == gameData.S0:
  225.                 # because this one is bigger
  226.                 cell_pos = (tl_pos[0] + col * cell_size + 0.5 * cell_size,
  227.                             tl_pos[1] + row * cell_size + 0.5 * cell_size)
  228.                 ui_rect = gameData.IMAGESDICT[content].get_rect()
  229.                 ui_rect.center = cell_pos
  230.                 DISPLAYSURF.blit(gameData.IMAGESDICT[content],ui_rect)
  231.                 continue
  232.  
  233.             cell_pos = (tl_pos[0] + col * cell_size,
  234.                         tl_pos[1] + row * cell_size)
  235.             DISPLAYSURF.blit(gameData.IMAGESDICT[content],
  236.                              (cell_pos[0], cell_pos[1]))
  237.  
  238.  
  239. def drawBoxPanal():
  240.     '''
  241.    draw the bottom left box area and buttons
  242.    '''
  243.     # draw bg
  244.     pygame.draw.rect(DISPLAYSURF, colors.UI_BG, gameData.Box_Pos)
  245.     pygame.draw.rect(DISPLAYSURF, colors.BLACK,gameData.Box_Pos,1)
  246.  
  247.  
  248.     # draw text
  249.     gameData.txt_box[0] = 'Ship Remain: %i - %i' % (gameData.shipCount['p1'],
  250.                                                   gameData.shipCount['p2'])
  251.     tl = gameData.Box_txt_Pos
  252.     margin = 25
  253.     line = 1
  254.     for txt in gameData.txt_box:
  255.         txt_Description, txt_DescriptionRect = \
  256.             makeTextObjs(txt, gameData.BASICFONT,colors.BLACK)
  257.         txt_DescriptionRect.center = (tl[0], tl[1]+margin*line)
  258.         DISPLAYSURF.blit(txt_Description, txt_DescriptionRect)
  259.         line += 1
  260.  
  261.  
  262.     # draw button
  263.     pygame.draw.rect(DISPLAYSURF, colors.WHITE, gameData.btnConcede_Rect)
  264.     pygame.draw.rect(DISPLAYSURF, colors.BLACK,gameData.btnConcede_Rect,1)
  265.     txt_Description, txt_DescriptionRect = \
  266.         makeTextObjs('CONCEDE', gameData.BASICFONT, colors.BLACK)
  267.     txt_DescriptionRect.center = gameData.btnConcede_Rect.center
  268.     DISPLAYSURF.blit(txt_Description, txt_DescriptionRect)
  269.  
  270.     pygame.draw.rect(DISPLAYSURF, colors.WHITE, gameData.btnNext_Rect)
  271.     pygame.draw.rect(DISPLAYSURF, colors.BLACK, gameData.btnNext_Rect, 1)
  272.     txt_Description, txt_DescriptionRect = \
  273.         makeTextObjs('NEXT TURN', gameData.BASICFONT, colors.BLACK)
  274.     txt_DescriptionRect.center = gameData.btnNext_Rect.center
  275.     DISPLAYSURF.blit(txt_Description, txt_DescriptionRect)
  276.  
  277.  
  278. def drawShipSelectedBox(player='p1'):
  279.     '''
  280.    Highlight the currently selected ship
  281.    '''
  282.     currentShip = gameData.shipSelected
  283.     if currentShip not in gameData.shipImagePos[player]: return
  284.     orient = gameData.ship[player][currentShip][0]
  285.     positionList = gameData.shipImagePos[player][currentShip]
  286.  
  287.     if orient == gameData.HORIZONTAL:
  288.         width = len(gameData.ships[currentShip]) * gameData.boardUI_cellSize
  289.         height = gameData.boardUI_cellSize
  290.     else:
  291.         height = len(gameData.ships[currentShip]) * gameData.boardUI_cellSize
  292.         width = gameData.boardUI_cellSize
  293.  
  294.     pos = convertBoardToPixel(positionList[0][0], positionList[0][1])
  295.     rect = pygame.Rect(pos[0],pos[1],width,height)
  296.     pygame.draw.rect(DISPLAYSURF, colors.RED, rect, 2)
  297.  
  298.  
  299.  
  300. def drawGameUI(player='p1'):
  301.     '''
  302.    redraw the game UI
  303.    '''
  304.     DISPLAYSURF.fill(colors.BG_BLUE)
  305.  
  306.     # draw bg
  307.     DISPLAYSURF.blit(gameData.IMAGESDICT['game_BG'],(0,0))
  308.  
  309.     # draw board
  310.     if VIEW_ALL: drawBoard(gameData.board, gameData.boardUI_TL_Pos)
  311.     else: drawBoard(gameData.view['p1'], gameData.boardUI_TL_Pos)
  312.  
  313.     # draw ships images
  314.     drawShipImages(player)
  315.  
  316.     # draw overlay UI
  317.     drawUIOverlay(player)
  318.     if VIEW_COMPUTER_OVERLAY: drawUIOverlay('p2')
  319.  
  320.     # draw SHIP SELECTED BOX
  321.     drawShipSelectedBox()
  322.  
  323.     # draw other UI
  324.     drawEnergyBar()
  325.     drawSkillsPanal(gameData.skill['p1'])
  326.     drawBoxPanal()
  327.  
  328.  
  329. def getRandomOrientation():
  330.     '''
  331.    randomly return an orientation (h/v)
  332.    '''
  333.     if random.randint(0,1) == 1:
  334.         return gameData.HORIZONTAL
  335.     else:
  336.         return gameData.VERTICAL
  337.  
  338.  
  339. def checkValidPosition(board, assumedShipPos):
  340.     '''
  341.    return True if all shipPos is on board
  342.    and don't collide with other ship
  343.    '''
  344.     for row,col in assumedShipPos:
  345.         if not 0<=row<gameData.row or not 0<=col<gameData.col:
  346.             return False
  347.         # now it's on board. check collision
  348.         if board[row][col] != '':
  349.             return False
  350.  
  351.     return True
  352.  
  353.  
  354. def putShipOnBoard(board, shipPos_list, ship_name, player, orientation,
  355.                    validate=True):
  356.     '''
  357.    merge all element in shipPos_list on to board
  358.    WILL OVERRIDE EXISTING CELL DATA
  359.    validate will check if ship is in dict; don't use it when init.
  360.    '''
  361.  
  362.     if validate == True:
  363.         if ship_name not in gameData.shipImagePos[player] or\
  364.                ship_name not in gameData.ship[player]:
  365.             return
  366.  
  367.     index = 0
  368.     for each in shipPos_list:
  369.         if each == False:
  370.             row,col = gameData.shipImagePos[player][ship_name][index]
  371.             status = gameData.STATUS_HIT
  372.         else:
  373.             row = each[0]
  374.             col = each[1]
  375.             status = gameData.STATUS_NORMAL
  376.  
  377.         board[row][col] = '%s_%s_%s_%s_%s' % \
  378.                           (str(player), str(ship_name), str(orientation),
  379.                            str(index),str(status))
  380.         index += 1
  381.  
  382.  
  383. def placeSubmarine(player, board):
  384.     '''
  385.    randomly update submarine pos on board and return position list
  386.    '''
  387.     length = len(gameData.ships[gameData.SUBMARINE])
  388.     orientation = gameData.VERTICAL
  389.     colBoundary = (0,gameData.col-1)
  390.     row = gameData.row-length if player == 'p1' else 0
  391.     tlPos = (row, random.randint(*colBoundary))
  392.  
  393.     # assured OK position so no need to validate
  394.     assumedShipPos = [(tlPos[0] + i, tlPos[1])
  395.                       for i in range(length)]
  396.     assumedShipPos.insert(0, orientation)
  397.  
  398.     putShipOnBoard(board, assumedShipPos[1:], gameData.SUBMARINE,
  399.                    player,orientation, False)
  400.     return assumedShipPos
  401.  
  402.  
  403. def getRandomShipLocation(player):
  404.     '''
  405.    randomly deploy ship and returns the ship dictionary
  406.    will only deploy ship at the players end
  407.  
  408.    boundary is [lower,upper]
  409.    '''
  410.     # set up
  411.     res_dict = dict()
  412.     board = [['' for col in xrange(gameData.col)]
  413.              for row in xrange(gameData.row)]
  414.     colBoundary = (0,gameData.col-1)
  415.     if player == 'p2': rowBoundary = (1,gameData.row/2-1) # ai bonus
  416.     else: rowBoundary = (gameData.row/2, gameData.row-1)
  417.  
  418.     # First place submarine and update board
  419.     res_dict[gameData.SUBMARINE] = placeSubmarine(player, board)
  420.     # Then other ships
  421.     for each_ship in gameData.ships:
  422.         if each_ship == gameData.SUBMARINE: continue
  423.         # place each ship into the board
  424.         length = len(gameData.ships[each_ship])
  425.         valid = False
  426.         while not valid:
  427.             orientation = getRandomOrientation()
  428.             tlPos = (random.randint(*rowBoundary), random.randint(*colBoundary))
  429.             if orientation == gameData.HORIZONTAL:
  430.                 assumedShipPos = [(tlPos[0], tlPos[1] + i)
  431.                                   for i in range(length)]
  432.             else:
  433.                 # VERTICAL also needs to check if across the boarder
  434.                 if tlPos[0]+length-1 >= rowBoundary[1]: continue
  435.                 assumedShipPos = [(tlPos[0] + i, tlPos[1])
  436.                                   for i in range(length)]
  437.             if checkValidPosition(board,assumedShipPos):
  438.                 res_dict[each_ship] = [orientation]
  439.                 putShipOnBoard(board, assumedShipPos, each_ship,
  440.                                player, orientation, False)
  441.                 res_dict[each_ship].extend(assumedShipPos)
  442.                 valid = True
  443.     return res_dict
  444.  
  445.  
  446. def setShipLocation():
  447.     '''
  448.    This sets the ship location for both players.
  449.    Currently all will be random
  450.    '''
  451.     # get ship location
  452.     # {'submarine': ['vertical', (8, 7), (9, 7), (10, 7)],
  453.     # 'battleship': ['vertical', (11, 3), (11, 4), (11, 5), (11, 6)]}
  454.     gameData.ship['p1'] = getRandomShipLocation('p1')
  455.     gameData.ship['p2'] = getRandomShipLocation('p2')
  456.  
  457.  
  458.  
  459.     # merge to shipImagePos to track images
  460.     for player in gameData.ship:
  461.         for each_name in gameData.ship[player]:
  462.             gameData.shipImagePos[player][each_name] =\
  463.                 copy.deepcopy(gameData.ship[player][each_name][1:])
  464.  
  465.     # merge to the board
  466.     for player in gameData.ship:
  467.         ship_dict = gameData.ship[player]
  468.         for each_name in ship_dict:
  469.             each_posList = ship_dict[each_name]
  470.             putShipOnBoard(gameData.board, each_posList[1:], each_name,
  471.                            player, each_posList[0], False)
  472.             # rotate image if necessary
  473.             if each_posList[0] == gameData.VERTICAL and player == 'p1':
  474.                 gameData.IMAGESDICT[each_name] = \
  475.                     pygame.transform.rotate(gameData.IMAGESDICT[each_name],90)
  476.  
  477.  
  478.  
  479. def convertBoardToPixel(row, col):
  480.     '''
  481.    return the pixel(true) cord of the TL of board location
  482.    :return: (x,y)
  483.    '''
  484.     x = gameData.boardUI_TL_Pos[0] + col * gameData.boardUI_cellSize
  485.     y = gameData.boardUI_TL_Pos[1] + row * gameData.boardUI_cellSize
  486.     return (x,y)
  487.  
  488.  
  489. def readCellContent(board, row, col):
  490.     '''
  491.    Decode the content stored in the input location of the board
  492.    :return: [player,shipName,orientation,posIndex, status]
  493.    '''
  494.     # format: 'player-shipName-orientation-posIndex-status'
  495.     try:
  496.         content = board[row][col]
  497.         res = content.split('_')
  498.         return res
  499.     except:
  500.         print "Exception at readCellContent (board, row, col)", board, row, col
  501.         return None
  502.  
  503.  
  504. def updateView(player):
  505.     '''
  506.    This updates the player's view
  507.    '''
  508.     # update player's ship info from ship[player]
  509.     gameData.view[player] = buildBoard()
  510.     for ship_name in gameData.ship[player]:
  511.         ship_data = gameData.ship[player][ship_name]
  512.         putShipOnBoard(gameData.view[player],
  513.                        ship_data[1:],ship_name,
  514.                        player, ship_data[0])
  515.     # update additional view
  516.     for row in range(len(gameData.revealed[player])):
  517.         for col in range(len(gameData.revealed[player][0])):
  518.             if gameData.revealed[player][row][col] == True:
  519.                 gameData.view[player][row][col] = gameData.board[row][col]
  520.  
  521.  
  522. def updateUIOverLay(player):
  523.     '''
  524.    update the UIOverLay for that player
  525.    remove 'recents' activity and change 'just' to 'recent' activity
  526.    :param player:
  527.    '''
  528.     board = copy.deepcopy(gameData.UIOverLay[player])
  529.     for row in range(len(board)):
  530.         for col in range(len(board[0])):
  531.             content = board[row][col]
  532.             if content == gameData.D0: board[row][col] = gameData.D1
  533.             elif content == gameData.H0: board[row][col] = gameData.H1
  534.             elif content == gameData.S0: board[row][col] = gameData.S1
  535.             elif content == gameData.M0: board[row][col] = gameData.M1
  536.             elif content == gameData.T0: board[row][col] = gameData.T1
  537.             elif content == gameData.D1 or content == gameData.H1\
  538.                 or content == gameData.S1 or content == gameData.M1\
  539.                     or content == gameData.T1:
  540.                 board[row][col] = ''
  541.     gameData.UIOverLay[player] = board
  542.  
  543.  
  544.  
  545. def buildBoard(fill=None):
  546.     '''
  547.    return an empty board
  548.    :return:
  549.    '''
  550.     if fill == None: fill = ''
  551.     return [[fill for col in xrange(gameData.col)]
  552.              for row in xrange(gameData.row)]
  553.  
  554.  
  555. def updateBoard():
  556.     '''
  557.    update gameData.board with the latest ship data of all two players
  558.    WILL flush the board
  559.    '''
  560.     gameData.board = buildBoard()
  561.     for player in gameData.ship:
  562.         ship_dict = gameData.ship[player]
  563.         for each_name in ship_dict:
  564.             each_shipData = ship_dict[each_name]
  565.             putShipOnBoard(gameData.board, each_shipData[1:], each_name,
  566.                            player, each_shipData[0])
  567.  
  568. def checkIfGameOver():
  569.     '''
  570.    check if any player lost all ships/ any ships reach enemy bot row
  571.    update gameData.isGameOver
  572.    '''
  573.     if gameData.shipCount['p1'] == gameData.shipCount['p2'] == 0:
  574.         gameData.isGameOver = True
  575.         gameData.winner = 'draw'
  576.         return
  577.     if gameData.shipCount['p1'] == 0:
  578.         gameData.isGameOver = True
  579.         gameData.winner = 'p2'
  580.         return
  581.     if gameData.shipCount['p2'] == 0:
  582.         gameData.isGameOver = True
  583.         gameData.winner = 'p1'
  584.         return
  585.  
  586.     # check ship reached end line
  587.     for player in ('p1','p2'):
  588.         endRow = 0 if player == 'p1' else gameData.row-1
  589.         for ship_name in gameData.shipImagePos[player]:
  590.             for points in gameData.shipImagePos[player][ship_name]:
  591.                 if points[0] == endRow:
  592.                     gameData.winner = player
  593.                     gameData.isGameOver = True
  594.                     return
  595.     gameData.isGameOver = False
  596.  
  597.  
  598.  
  599.  
  600. def destroyShip(player, ship_name):
  601.     '''
  602.    Destroy the given ship.
  603.    Will remove ship name from gameData.ship and gameData.shipImagePos
  604.    Also decrease the count and update D overlay
  605.    '''
  606.     print 'deleting gameData.ship: ',player, ship_name
  607.     for each_pos in gameData.shipImagePos[player][ship_name]:
  608.         # notify both player
  609.         gameData.UIOverLay['p1'][each_pos[0]][each_pos[1]] = gameData.D0
  610.         gameData.UIOverLay['p2'][each_pos[0]][each_pos[1]] = gameData.D0
  611.  
  612.     del gameData.ship[player][ship_name]
  613.     del gameData.shipImagePos[player][ship_name]
  614.     gameData.shipCount[player] -= 1
  615.  
  616.     if gameData.shipSelected == ship_name:
  617.         gameData.shipSelected = ''
  618.  
  619.     # also increase the energy for that player!
  620.     enemy = 'p2' if player == 'p1' else 'p1'
  621.     gameData.maxEnergy[enemy] += 1
  622.     if gameData.maxEnergy[enemy] >= gameData.MAX_ENERGY:
  623.         gameData.maxEnergy[enemy] = gameData.MAX_ENERGY
  624.  
  625.     checkIfGameOver()
  626.  
  627.  
  628. def randomlyTakeDamage(player, ship_name, amount):
  629.     '''
  630.    randomly take amount damage to the ship
  631.    modify gameData.ship data
  632.    :return: None
  633.    '''
  634.     ship_data = copy.deepcopy(gameData.ship[player][ship_name])
  635.     ship_status = copy.deepcopy(ship_data[1:])
  636.     if amount >= len(filter(None, ship_status)):
  637.         destroyShip(player, ship_name)
  638.         return
  639.  
  640.  
  641.     while amount > 0:
  642.         available_index = []
  643.         for i in range(len(ship_status)):
  644.             if ship_status[i] != False: available_index.append(i)
  645.         # random pick an i to hit
  646.         choice = random.choice(available_index)
  647.  
  648.         ship_status[choice] = False
  649.         amount -= 1
  650.  
  651.  
  652.  
  653.     ship_data[1:] = ship_status
  654.     gameData.ship[player][ship_name] = ship_data
  655.  
  656. def dealDamageToCell(row,col,ui_player):
  657.     '''
  658.    mark the given cell hit and update ship_data
  659.    check destroyship()
  660.    :param row:
  661.    :param col:
  662.    :return:
  663.    '''
  664.     if gameData.board[row][col] == '':
  665.         # attack missed
  666.         gameData.UIOverLay[ui_player][row][col] = gameData.M0
  667.         return False
  668.     else:
  669.         cell = readCellContent(gameData.board,row,col)
  670.         # [player, shipName, orientation, posIndex, status]
  671.  
  672.         # update UI
  673.         gameData.UIOverLay[ui_player][row][col] = gameData.H0
  674.  
  675.         # update ship status
  676.         gameData.ship[cell[0]][cell[1]][int(cell[3])+1] = False
  677.  
  678.         # check if ship destoryed
  679.         if all([each == False for each in gameData.ship[cell[0]][cell[1]][1:]]):
  680.             print 'Last Hit!'
  681.             destroyShip(cell[0], cell[1])
  682.         return True
  683.  
  684.  
  685. def scanArea(player, row, col):
  686.     '''
  687.    reveal the 3*3 area for one turn
  688.    '''
  689.     rows = range(row-1,row+2)
  690.     cols = range(col-1,col+2) # -1 col +1
  691.     for each_row in rows:
  692.         for each_col in cols:
  693.             if 0<=each_row<gameData.row and 0<=each_col<gameData.col:
  694.                 gameData.revealed[player][each_row][each_col] = True
  695.                 # update UI Overlay
  696.                 enemy = 'p1' if gameData.currentPlayer== 'p2' else 'p2'
  697.                 ship_name = isRowColOnOwnShip(each_row,each_col,enemy)
  698.                 if ship_name != '':
  699.                     if (each_row, each_col) in \
  700.                         gameData.ship[enemy][ship_name]:
  701.                         # then cell is not damaged
  702.                         # print "Update UI at ", each_row,each_col
  703.                         gameData.UIOverLay[player][each_row][each_col]\
  704.                             = gameData.T0
  705.  
  706.     updateView(player)
  707.  
  708.  
  709. def action_scan(row, col, player):
  710.     '''
  711.    Handle scan event
  712.    Also update UI and view
  713.    '''
  714.     if gameData.currentEnergy[player] < gameData.SCAN_MP[player]:
  715.         #'Not enough energy'
  716.         return
  717.     scanArea(player, row, col)
  718.     gameData.UIOverLay[player][row][col] = gameData.S0
  719.     gameData.currentEnergy[player] -= gameData.SCAN_MP[player]
  720.     updateView(player)
  721.  
  722.  
  723. def action_Fire(row, col, player):
  724.     '''
  725.    Handle one fire action and reduce mana
  726.    '''
  727.     if gameData.currentEnergy[player] < gameData.FIRE_MP[player]:
  728.         #'Not enough energy'
  729.         return
  730.  
  731.     dealDamageToCell(row,col,player)
  732.     updateBoard()
  733.     updateView('p1')
  734.     updateView('p2')
  735.  
  736.     gameData.currentEnergy[player] -= gameData.FIRE_MP[player]
  737.  
  738.  
  739. def action_move(player, ship_name, drow, dcol):
  740.     if gameData.currentEnergy[player] < gameData.MOVE_MP[player]:
  741.         print 'Not enough energy', gameData.currentEnergy[player]
  742.         return
  743.     if moveShip(ship_name, player, drow, dcol):
  744.         gameData.currentEnergy[player] -= gameData.MOVE_MP[player]
  745.  
  746.  
  747. def handleShipCollision(shipAData, newPos, oldPos,playerA,shipNameA):
  748.     '''
  749.    checks if the shipA(first parameter)'s newPos hits any other ship
  750.    if so handle collision damage and updates affected ship
  751.    :param shipAData: [ori, pos1,pos2,...]
  752.    :param newPos: assumed new ship pos list
  753.    :return: False if destroyed
  754.    '''
  755.     affectedShipPoints = []
  756.  
  757.     for row,col in newPos:
  758.         if (row,col) in oldPos: continue
  759.         # assume if cell not empty then must hit others
  760.         if gameData.board[row][col] != '':
  761.             decodedData = readCellContent(gameData.board, row, col)
  762.             # [player, shipname, orientation, pos_index, status]
  763.             affectedShipPoints.append(decodedData)
  764.     if affectedShipPoints == []:
  765.         return newPos
  766.  
  767.     # now we find all collided ships
  768.  
  769.     # make a ship list
  770.     affectedShip = {each[0]+'_'+each[1] for each in affectedShipPoints}
  771.     # [[player, ship], ...]
  772.     affectedShip = [each.split('_') for each in affectedShip]
  773.  
  774.     att = len(filter(None, shipAData[1:])) # damaged ship part = False
  775.  
  776.     # find combined def strength
  777.     defence = 0
  778.     for each in affectedShip:
  779.         # count non-False item in each ship's pos data
  780.         defence += len(filter(None, gameData.ship[each[0]][each[1]][1:]))
  781.  
  782.  
  783.     # handleShipCollsion
  784.     if att == defence:
  785.         destroyShip(playerA, shipNameA)
  786.         # destroy all affected ships
  787.         for each in affectedShip:
  788.             destroyShip(each[0], each[1])
  789.         return False # notify that shipA destroyed
  790.     elif att > defence:
  791.         randomlyTakeDamage(playerA, shipNameA, defence)
  792.         for each in affectedShip:
  793.             destroyShip(each[0], each[1])
  794.     else: # att < defence
  795.         destroyShip(playerA, shipNameA)
  796.         for each in affectedShip:
  797.             randomlyTakeDamage(each[0], each[1], att)
  798.         return False
  799.     return True
  800.  
  801.  
  802. def checkIfOKMove(oldPos, newPos):
  803.     '''
  804.    check if a move don't go out of boundary / run into own ships
  805.    '''
  806.     for row,col in newPos:
  807.         # exclude previous self location
  808.         if (row,col) in oldPos: continue
  809.         if not 0<=row<gameData.row or not 0<=col<gameData.col:
  810.             return False
  811.         # now it's on board. check collision
  812.  
  813.         # check if going to hit own ship
  814.         if isRowColOnOwnShip(row,col,gameData.currentPlayer)!='':
  815.             return False
  816.  
  817.     return True
  818.  
  819.  
  820. def moveShip(ship_name, player, drow, dcol):
  821.     '''
  822.    move player's ship for drow / dcol with invalid position rejected
  823.    and collision handle / win check
  824.    update board and (shipImagePos)
  825.    :return: bool: isMoveSuccessful
  826.    '''
  827.     if ship_name not in gameData.ship[player]: return
  828.     ship_data = copy.deepcopy(gameData.ship[player][ship_name])
  829.     oldPos = gameData.shipImagePos[player][ship_name]
  830.     newPos = [(each[0]+drow,each[1]+dcol) for each in oldPos]
  831.  
  832.  
  833.     if checkIfOKMove(oldPos, newPos):
  834.         # checkCollision
  835.         if handleShipCollision(ship_data,newPos,oldPos, player,ship_name):
  836.             # if ship lives
  837.             gameData.shipImagePos[player][ship_name] = newPos
  838.             for i in range(1,len(gameData.ship[player][ship_name])):
  839.                 if gameData.ship[player][ship_name][i] != False:
  840.                     gameData.ship[player][ship_name][i] = newPos[i-1]
  841.  
  842.         # draw to board
  843.         updateBoard()
  844.         updateView(player)
  845.         checkIfGameOver()
  846.  
  847.         return True
  848.     return False
  849.  
  850.  
  851. def clearRevealedArea(player):
  852.     '''
  853.    clear gameData.revealed for that player
  854.    '''
  855.     gameData.revealed[player] = buildBoard(False)
  856.  
  857.  
  858. def getMouseBoardPosition(posx, posy):
  859.     '''
  860.    :return: the (row,col) on the board, else False
  861.    '''
  862.     boardPos = gameData.boardUI_TL_Pos
  863.     size = gameData.boardUI_cellSize
  864.     if not boardPos[0]<=posx<=boardPos[0] + gameData.col * size or\
  865.         not boardPos[1]<=posy<=boardPos[1] + gameData.row * size:
  866.         return False
  867.     else:
  868.         col = (posx - boardPos[0]) / size
  869.         row = (posy - boardPos[1]) / size
  870.  
  871.         return (row,col)
  872.  
  873.  
  874. def isRowColOnOwnShip(row, col, player):
  875.     '''
  876.    return the ship name if the row col is on player's ship,
  877.    empty string if not.
  878.    '''
  879.     cell_data = readCellContent(gameData.board, row,col)
  880.     # [player, shipName, orientation, posIndex, status]
  881.     if cell_data[0] == player:
  882.         return cell_data[1]
  883.  
  884.     return ''
  885.  
  886. def endTurn():
  887.     '''
  888.    end current player's turn and switch player's turn
  889.    also update the data for next turn
  890.    '''
  891.     player = gameData.currentPlayer
  892.     nextPlayer = 'p1' if player == 'p2' else 'p2'
  893.  
  894.     clearRevealedArea(player)
  895.     updateView(player)
  896.     updateView(nextPlayer)
  897.  
  898.     # update currentPlayer
  899.     gameData.currentPlayer = nextPlayer
  900.     gameData.currentEnergy[nextPlayer] = gameData.maxEnergy[nextPlayer]
  901.     updateUIOverLay(nextPlayer)
  902.     drawGameUI('p1')
  903.     gameData.shipSelected = ''
  904.  
  905.     # pygame.time.wait(500)
  906.     pygame.event.clear()
  907.  
  908.  
  909. def concede(player):
  910.     '''
  911.    Make the player concede and trigger game over
  912.    '''
  913.     enemy = 'p2' if player == 'p1' else 'p1'
  914.     gameData.winner = enemy
  915.     gameData.isGameOver = True
  916.  
  917.  
  918. def handleP1Events():
  919.     for event in pygame.event.get():
  920.         if event.type == QUIT: terminate()
  921.  
  922.         # events that's only available at players turn
  923.         if gameData.currentPlayer == 'p1':
  924.             # if currentMode == '':
  925.             #     # let player choose mode
  926.             #     checkForModeSelection(event)
  927.             # print 'P1 EVENT CHECKING'
  928.  
  929.             # FIRST check buttons hit or not
  930.             if event.type == MOUSEBUTTONUP\
  931.                     and gameData.btnConcede_Rect.collidepoint(event.pos):
  932.                 concede(gameData.currentPlayer)
  933.             elif event.type == MOUSEBUTTONUP \
  934.                         and gameData.btnNext_Rect.collidepoint(event.pos):
  935.                     endTurn()
  936.                     continue
  937.  
  938.             if event.type == KEYDOWN:
  939.                 if event.key == K_n:
  940.                     # another way to end turn
  941.                     endTurn()
  942.                     continue
  943.  
  944.                 # handle move
  945.                 if gameData.shipSelected != '':
  946.                     if event.key in (K_LEFT, K_a):
  947.                         action_move('p1', gameData.shipSelected, 0, -1)
  948.                     elif event.key in (K_RIGHT, K_d):
  949.                         action_move('p1', gameData.shipSelected, 0, 1)
  950.                     elif event.key in (K_UP, K_w):
  951.                         action_move('p1', gameData.shipSelected, -1, 0)
  952.                     elif event.key in (K_DOWN, K_s):
  953.                         action_move('p1', gameData.shipSelected, 1, 0)
  954.             if event.type == MOUSEBUTTONUP:
  955.                 pos = getMouseBoardPosition(event.pos[0], event.pos[1])
  956.                 if pos == False: continue
  957.                 if event.button == 1:
  958.                     # -------first check ship selection-------
  959.                     clicked_ship = isRowColOnOwnShip(pos[0], pos[1], 'p1')
  960.                     # returned ship name
  961.                     if clicked_ship != '':
  962.                         # Ship selected
  963.                         gameData.shipSelected = clicked_ship
  964.  
  965.                         continue
  966.  
  967.                     # -------then fire action-------
  968.  
  969.                     if pos != False:
  970.                         action_Fire(pos[0], pos[1], gameData.currentPlayer)
  971.                 elif event.button == 3: # right button
  972.                     action_scan(pos[0],pos[1], gameData.currentPlayer)
  973.  
  974.  
  975. def AI_updateTargets():
  976.     gameData.targets_pos = []
  977.     for row in range(len(gameData.UIOverLay['p2'])):
  978.         for col in range(len(gameData.UIOverLay['p2'][0])):
  979.             if gameData.UIOverLay['p2'][row][col] == gameData.T0 or \
  980.                             gameData.UIOverLay['p2'][row][col] == gameData.T1:
  981.                 gameData.targets_pos.append((row,col))
  982.  
  983.  
  984.  
  985.  
  986. def AI_turn():
  987.     '''
  988.    this handles all AI action
  989.    '''
  990.     if gameData.currentEnergy['p2'] >= min(gameData.FIRE_MP['p2'],
  991.                                              gameData.MOVE_MP['p2'],
  992.                                              gameData.SCAN_MP['p2'],):
  993.         rand = random.randint(0,100)
  994.         if 0<=rand<60:
  995.             # do fire action
  996.             f_rand = random.randint(0, 100)
  997.             if 0<=f_rand<30 and len(gameData.targets_pos) == 0:
  998.                 # do scan
  999.                 pos = (random.randint(1,gameData.row-2),
  1000.                        random.randint(1,gameData.col-2))
  1001.                 action_scan(pos[0],pos[1], 'p2')
  1002.                 AI_updateTargets()
  1003.                 print gameData.targets_pos
  1004.             else:
  1005.                 if len(gameData.targets_pos) > 0:
  1006.                     if len(gameData.targets_pos) == 1:
  1007.                         fireIndex = 0
  1008.                     else:
  1009.                         # multiple target
  1010.                         fireIndex = random.randint(0,
  1011.                                                    len(gameData.targets_pos)-1)
  1012.                     firePos = gameData.targets_pos[fireIndex]
  1013.                     action_Fire(firePos[0], firePos[1], 'p2')
  1014.                     # print 'Targetted', gameData.currentEnergy['p2'], firePos
  1015.                     gameData.targets_pos.pop(fireIndex)
  1016.                     gameData.UIOverLay['p2'][firePos[0]][firePos[1]] = ''
  1017.  
  1018.                 else:
  1019.                     firePos = (0,0)
  1020.                     while True: # get's the correct location
  1021.                         firePos = (random.randint(0,gameData.row-1),
  1022.                                    random.randint(0, gameData.col-1))
  1023.                         data = readCellContent(gameData.view['p2'],
  1024.                                                firePos[0],firePos[1])
  1025.                         # can't hit itself
  1026.                         if data[0] != 'p2': break
  1027.  
  1028.                     action_Fire(firePos[0], firePos[1], 'p2')
  1029.                     print 'Random', gameData.currentEnergy['p2'], firePos
  1030.         else:
  1031.             # do move action
  1032.             selectedShip_name = random.choice(gameData.ship['p2'].keys())
  1033.             dir = random.choice([(0,-1),(0,1),(1,0),(1,0),(1,0),])
  1034.             action_move('p2', selectedShip_name, dir[0], dir[1])
  1035.  
  1036.  
  1037.     else:
  1038.         endTurn()
  1039.         return
  1040.  
  1041.  
  1042. def showGameOverScreen():
  1043.     '''
  1044.    this shows the game over screen
  1045.    '''
  1046.     # draw bg
  1047.     DISPLAYSURF.blit(gameData.IMAGESDICT['win_BG'],gameData.win_bg_pos)
  1048.  
  1049.     # draw texts
  1050.     txt_first, txt_firstRect = \
  1051.         makeTextObjs('Game Over', gameData.FONT36, colors.WHITE)
  1052.     txt_firstRect.center = gameData.win_txt1_pos
  1053.     DISPLAYSURF.blit(txt_first, txt_firstRect)
  1054.  
  1055.     second = 'You WIN!' if gameData.winner == 'p1' else 'AI WIN!'
  1056.     txt_second, txt_secondRect = \
  1057.         makeTextObjs(second, gameData.FONT36, colors.WHITE)
  1058.     txt_secondRect.center = gameData.win_txt2_pos
  1059.     DISPLAYSURF.blit(txt_second, txt_secondRect)
  1060.  
  1061.     txt_third, txt_thirdRect = \
  1062.         makeTextObjs("Press any key to continue"
  1063.                      , gameData.BASICFONT, colors.WHITE)
  1064.     txt_thirdRect.center = gameData.win_txt3_pos
  1065.     DISPLAYSURF.blit(txt_third, txt_thirdRect)
  1066.  
  1067.  
  1068. def waitForKeyPressed(key=None):
  1069.     '''
  1070.    pause and wait for any key input.
  1071.    If key is not given, use all keys
  1072.    '''
  1073.     while True:
  1074.         for event in pygame.event.get():
  1075.             if event.type == QUIT: terminate()
  1076.             if event.type == KEYDOWN:
  1077.                 if key != None:
  1078.                     if event.key == key:
  1079.                         return
  1080.                 else:
  1081.                     return
  1082.         pygame.display.update()
  1083.         FPSCLOCK.tick(gameData.FPS)
  1084.  
  1085.  
  1086. def runGame():
  1087.     '''
  1088.    This is the main function for actual game part
  1089.    '''
  1090.     gameInit()
  1091.     setShipLocation()
  1092.     updateView('p1')
  1093.     updateView('p2')
  1094.     drawGameUI('p1')
  1095.  
  1096.  
  1097.     # Game States
  1098.  
  1099.     # choices are: "move"
  1100.     # currentMode = ''
  1101.  
  1102.     # Main Game Loop
  1103.     while True:
  1104.         updateView('p1')
  1105.         updateView('p2')
  1106.         drawGameUI('p1')
  1107.  
  1108.         if gameData.isGameOver == True:
  1109.             print 'Game Over', gameData.winner
  1110.             showGameOverScreen()
  1111.             waitForKeyPressed()
  1112.             return
  1113.  
  1114.         # game Continues
  1115.         elif gameData.currentPlayer == 'p2':
  1116.             # AI's move
  1117.             pygame.time.wait(500)
  1118.             AI_turn()
  1119.         else:
  1120.             # player's turn
  1121.             handleP1Events()
  1122.             # check if no energy
  1123.             if gameData.currentEnergy['p1'] < min(gameData.FIRE_MP['p1'],
  1124.                                                    gameData.MOVE_MP['p1'],
  1125.                                                    gameData.SCAN_MP['p1'], ):
  1126.                 pygame.time.wait(500)
  1127.                 endTurn()
  1128.  
  1129.  
  1130.         pygame.display.update()
  1131.         FPSCLOCK.tick(gameData.FPS)
  1132.  
  1133.  
  1134. def gameInit_UIOverLay():
  1135.     '''
  1136.    define UI OverLay elements
  1137.  
  1138.    D0 = damaged this turn
  1139.    D1 = damaged last turn etc.
  1140.    H = HIT
  1141.    M = MISSED
  1142.    S = SCANNED
  1143.    T = TARGET
  1144.    '''
  1145.     gameData.UIOverLay = {'p1': copy.deepcopy(gameData.board),
  1146.                           'p2': copy.deepcopy(gameData.board)}
  1147.     gameData.D0 = 'd0'
  1148.     gameData.D1 = 'd1'
  1149.     gameData.H0 = 'h0'
  1150.     gameData.H1 = 'h1'
  1151.     gameData.M0 = 'm0'
  1152.     gameData.M1 = 'm1'
  1153.     gameData.S0 = 's0'
  1154.     gameData.S1 = 's1'
  1155.     gameData.T0 = 't0'
  1156.     gameData.T1 = 't1'
  1157.  
  1158.  
  1159. def init_images():
  1160.     # Images
  1161.     gameData.IMAGESDICT = {
  1162.         'game_BG': pygame.image.load('data/game_BG.png'),
  1163.         'win_BG': pygame.image.load('data/win_bg.fw.png'),
  1164.         'scan1': pygame.image.load('data/scan1.fw.png'),
  1165.         'fire1': pygame.image.load('data/fire1.fw.png'),
  1166.         'move1': pygame.image.load('data/move1.fw.png'),
  1167.         'locked': pygame.image.load('data/locked.png'),
  1168.         'carrier': pygame.image.load('data/carrier.fw.png'),
  1169.         'battleship': pygame.image.load('data/battleship.fw.png'),
  1170.         'cruiser': pygame.image.load('data/cruiser.fw.png'),
  1171.         'destroyer': pygame.image.load('data/destroyer.fw.png'),
  1172.         'submarine': pygame.image.load('data/submarine.fw.png'),
  1173.         'm0': pygame.image.load('data/ui_m.fw.png'),
  1174.         'h0': pygame.image.load('data/ui_h.fw.png'),
  1175.         'd0': pygame.image.load('data/ui_d.fw.png'),
  1176.         's0': pygame.image.load('data/ui_s.fw.png'),
  1177.         't0': pygame.image.load('data/ui_t.fw.png'),
  1178.         'm1': pygame.image.load('data/ui_m1.fw.png'),
  1179.         'h1': pygame.image.load('data/ui_h1.fw.png'),
  1180.         'd1': pygame.image.load('data/ui_d1.fw.png'),
  1181.         's1': pygame.image.load('data/ui_s1.fw.png'),
  1182.         't1': pygame.image.load('data/ui_t1.fw.png'),
  1183.         }
  1184.  
  1185.  
  1186. def gameInit():
  1187.     # init_images
  1188.     init_images()
  1189.  
  1190.     # used to set up a game!
  1191.     gameData.board = buildBoard()
  1192.  
  1193.     gameData.view = {'p1': copy.deepcopy(gameData.board),
  1194.                      'p2': copy.deepcopy(gameData.board)}
  1195.  
  1196.     # noinspection PyTypeChecker
  1197.     gameData.revealed = {'p1': buildBoard(False),'p2': buildBoard(False)}
  1198.  
  1199.     # define game controllor variable
  1200.     gameData.isGameOver = False
  1201.     gameData.winner = 'p1'
  1202.     gameData.shipCount = {'p1': gameData.STARTSHIPCOUNT,
  1203.                           'p2': gameData.STARTSHIPCOUNT}
  1204.     gameData.shipSelected = ''
  1205.  
  1206.  
  1207.     # define game states
  1208.     gameData.currentPlayer = 'p1'
  1209.  
  1210.     # define resources
  1211.     gameData.skill = {'p1':['fire1','scan1','move1','',''],
  1212.                       'p2':['fire1','scan1','move1','','']}
  1213.  
  1214.     gameData.maxEnergy = {'p1': gameData.START_ENERGY,
  1215.                           'p2': gameData.START_ENERGY}
  1216.     gameData.currentEnergy = {'p1': gameData.maxEnergy['p1'],
  1217.                               'p2': gameData.maxEnergy['p2']}
  1218.  
  1219.     # set up initial MP
  1220.     gameData.FIRE_MP = {'p1': 2,'p2': 2}
  1221.     gameData.MOVE_MP = {'p1': 2,'p2': 2}
  1222.     gameData.SCAN_MP = {'p1': 3,'p2': 3}
  1223.  
  1224.  
  1225.     # define UI elements
  1226.     gameInit_UIOverLay()
  1227.  
  1228.  
  1229.     gameData.txt_skillDescription_D = ['Left Click to Fire',
  1230.                                        'Right Click to Scan',
  1231.                                        'Click ship to Move']
  1232.     gameData.txt_skillDescription = \
  1233.         copy.deepcopy(gameData.txt_skillDescription_D)
  1234.  
  1235.     gameData.txt_box_D = ['Ship Remain: 5-5']
  1236.     gameData.txt_box = \
  1237.         copy.deepcopy(gameData.txt_box_D)
  1238.  
  1239.  
  1240.     gameData.txt_EB = 'Your Turn'
  1241.  
  1242.     # will be updated immediately after
  1243.     gameData.ship = {'p1':{},
  1244.                      'p2':{}}
  1245.     gameData.shipImagePos = {'p1':{},
  1246.                             'p2':{}}
  1247.  
  1248.     # for AI use
  1249.     gameData.targets_pos = []
  1250.  
  1251.  
  1252.  
  1253. def init_ships():
  1254.     '''
  1255.    used to generate ship modal for games
  1256.    '''
  1257.     gameData.HORIZONTAL = 'h'
  1258.     gameData.VERTICAL = 'v'
  1259.     gameData.STATUS_HIT = 'h'
  1260.     gameData.STATUS_NORMAL = 'n'
  1261.     # capitals in gameData is ship name
  1262.     gameData.CARRIER = 'carrier'
  1263.     gameData.BATTLESHIP = 'battleship'
  1264.     gameData.CRUISER = 'cruiser'
  1265.     gameData.DESTROYER = 'destroyer'
  1266.     gameData.SUBMARINE = 'submarine'
  1267.  
  1268.     # ships store the actual design
  1269.     gameData.ships = dict()
  1270.     gameData.ships[gameData.CARRIER] = [True] * 5
  1271.     gameData.ships[gameData.BATTLESHIP] = [True] * 4
  1272.     gameData.ships[gameData.CRUISER] = [True] * 3
  1273.     gameData.ships[gameData.DESTROYER] = [True] * 3
  1274.     gameData.ships[gameData.SUBMARINE] = [True] * 2
  1275.  
  1276.     gameData.STARTSHIPCOUNT = len(gameData.ships)
  1277.  
  1278.  
  1279. def init():
  1280.     '''
  1281.    initialize all variable before game starts
  1282.    '''
  1283.     # essentials
  1284.     gameData.FPS = 30
  1285.     pygame.display.set_caption('BattleShip')
  1286.  
  1287.     # game settings
  1288.     gameData.START_ENERGY = 6
  1289.     gameData.MAX_ENERGY = 8
  1290.  
  1291.     # board
  1292.     gameData.row = 14
  1293.     gameData.col = 10
  1294.     gameData.boardUI_cellSize = 36
  1295.     gameData.boardUI_TL_Pos = (397, 48)
  1296.  
  1297.     # UIs
  1298.     gameData.showHelp = False
  1299.  
  1300.     gameData.BG_Pos = (-45,-50,921,679)
  1301.     gameData.EB_Pos = (39,40,324,61)
  1302.     gameData.Skills_Pos = (40,128,323,201)
  1303.     gameData.Skills_txt_Pos = (193,223)
  1304.     gameData.Box_txt_Pos = (201,381)
  1305.     gameData.Box_Pos = (40,349,323,211)
  1306.     gameData.btnConcede_Rect = pygame.Rect(71,494,120,48)
  1307.     gameData.btnNext_Rect = pygame.Rect(212,494,120,48)
  1308.     gameData.win_bg_pos = (0,124)
  1309.     gameData.win_txt1_pos = (524,210)
  1310.     gameData.win_txt2_pos = (524,275)
  1311.     gameData.win_txt3_pos = (580,442)
  1312.  
  1313.     # Skill Boxes
  1314.     gameData.skillBox = [(57,141,50,50),
  1315.                          (117,141,50,50),
  1316.                          (177,141,50,50),
  1317.                          (237,141,50,50),
  1318.                          (297,141,50,50)]
  1319.     # Skill Names
  1320.     gameData.blankSkillName = 'locked'
  1321.  
  1322.  
  1323.  
  1324.     # ships
  1325.     init_ships()
  1326.  
  1327.     # fonts
  1328.     gameData.BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
  1329.     gameData.FONT36 = pygame.font.SysFont('Arial', 36)
  1330.  
  1331.  
  1332.  
  1333.     # colors
  1334.     colors.WHITE = (255,255,255)
  1335.     colors.BLACK = (0,0,0)
  1336.     colors.RED = (255,0,0)
  1337.     colors.GREEN = (15, 255, 118)
  1338.     colors.EMPTY_CELL = (225, 245, 254) # pale blue
  1339.  
  1340.     colors.P1_SHIP_BG = (45, 80, 133)
  1341.     colors.P2_SHIP_BG = (172, 57, 57)
  1342.  
  1343.     colors.DEEP_BLUE = (40, 53, 147)
  1344.     colors.CELL_BOARDERLINE = (40, 53, 147)
  1345.     colors.BG_BLUE = (144, 202, 249)
  1346.     colors.UI_BOARDER = (129, 136, 152)
  1347.     colors.EB_BG = (249, 236, 209)
  1348.     colors.EB_BG2 = (160, 28, 28)
  1349.  
  1350.     colors.EB_BOARDER = (129, 136, 152)
  1351.     colors.UI_BG = (232, 234, 246)
  1352.  
  1353. def main():
  1354.     global gameData, colors, DISPLAYSURF, FPSCLOCK
  1355.     gameData = Struct()
  1356.     colors = Struct()
  1357.     pygame.init()
  1358.     gameData.windowWidth = 800
  1359.     gameData.windowHeight = 600
  1360.     FPSCLOCK = pygame.time.Clock()
  1361.     DISPLAYSURF = pygame.display.set_mode((gameData.windowWidth,
  1362.                                            gameData.windowHeight))
  1363.     init()
  1364.  
  1365.     # Main Scene Loop
  1366.     while True:
  1367.         # start 'scenes' in the game
  1368.         runStartScreen()
  1369.         runGame()
  1370.  
  1371.  
  1372.  
  1373.  
  1374.  
  1375.  
  1376. # ---------------- RUN ----------------
  1377. if __name__ == '__main__':
  1378.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement