Python Forum
Message='int' object is not subscriptable
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Message='int' object is not subscriptable
#1
I'm trying to make battle ships in python and for program works for the most part apart from supposedly randomly when the computer is generating coordinates to attack the player and misses instead of creating a coordinate it creates a number.
The coordinate generator works by having a list ["a","b","c","d","e","f","g","h"] then generating a random number for the row (0,7) which is then used to take a letter from the list e.g. if the random number is 2 it would take c from the list. Then it would generate a second number for the column (1,8) then put them together to create a coordinate. e.g. c5. This works a lot of the time but at some points instead of getting c5 it would give 25 and i'm not sure why as it doesn't do it all the time.

import sys
import random
import time
# GUI
print("Welcome to battle boats\n1. New game\n2. Continue game\n3. Quit")
startMenuInput = input("Enter 1, 2 or 3 to make your choice: ")

## Subroutines here

def printGrid(nums,lines,a,b,c,d,e,f,g,h):
    print(*nums, sep = '')
    print(*lines, sep = '')
    print(*a, sep='')
    print(*b, sep='')
    print(*c, sep='')
    print(*d, sep='')
    print(*e, sep='')
    print(*f, sep='')
    print(*g, sep='')
    print(*h, sep='')
    print("\n")

def printCOMGrid(COMnums,COMlines,COMa,COMb,COMc,COMd,COMe,COMf,COMg,COMh):
    print(*COMnums, sep = '')
    print(*COMlines, sep = '')
    print(*COMa, sep='')
    print(*COMb, sep='')
    print(*COMc, sep='')
    print(*COMd, sep='')
    print(*COMe, sep='')
    print(*COMf, sep='')
    print(*COMg, sep='')
    print(*COMh, sep='')
    print("\n")

def printTargetTracker(TTnums,TTlines,TTa,TTb,TTc,TTd,TTe,TTf,TTg,TTh):
    print(*TTnums, sep = '')
    print(*TTlines, sep = '')
    print(*TTa, sep='')
    print(*TTb, sep='')
    print(*TTc, sep='')
    print(*TTd, sep='')
    print(*TTe, sep='')
    print(*TTf, sep='')
    print(*TTg, sep='')
    print(*TTh, sep='')
    print("\n")

def newGame():
    enteredCoordinates = []
    COMenteredCoorindates = []
    COMattackCoordinates = []

    nums = ["   "," 1 ","  2 ","  3 ","  4 ","  5 ","  6 ","  7 ","  8 "]
    lines = ["  ---------------------------------"]
    a = ["a| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    b = ["b| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    c = ["c| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    d = ["d| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    e = ["e| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    f = ["f| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    g = ["g| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    h = ["h| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    
    COMnums = ["   "," 1 ","  2 ","  3 ","  4 ","  5 ","  6 ","  7 ","  8 "]
    COMlines = ["  ---------------------------------"]
    COMa = ["a| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    COMb = ["b| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    COMc = ["c| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    COMd = ["d| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    COMe = ["e| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    COMf = ["f| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    COMg = ["g| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    COMh = ["h| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]

    TTnums = ["   "," 1 ","  2 ","  3 ","  4 ","  5 ","  6 ","  7 ","  8 "]
    TTlines = ["  ---------------------------------"]
    TTa = ["a| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    TTb = ["b| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    TTc = ["c| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    TTd = ["d| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    TTe = ["e| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    TTf = ["f| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    TTg = ["g| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]
    TTh = ["h| ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] ","[ ] "]

    
    for i in range (5):
        alreadyEntered = True
        placementCoordinate = input("Enter the coordinates of your boat: ")
        print("\n")
        while alreadyEntered == True:
            if placementCoordinate in enteredCoordinates:
                alreadyEntered = True
                placementCoordinate = input("Coordinate already entered, enter another coordinate: ")
            else:
                alreadyEntered = False
        if alreadyEntered == False:
            enteredCoordinates.append(placementCoordinate)
            row = placementCoordinate[0]
            column = placementCoordinate[1]
            column = int(column)
            if row == "a":
                a.pop(column)
                a.insert(column,"[*] ")
            if row == "b":
                b.pop(column)
                b.insert(column,"[*] ")
            if row == "c":
                c.pop(column)
                c.insert(column,"[*] ")
            if row == "d":
                d.pop(column)
                d.insert(column,"[*] ")
            if row == "e":
                e.pop(column)
                e.insert(column,"[*] ")
            if row == "f":
                f.pop(column)
                f.insert(column,"[*] ")
            if row == "g":
                g.pop(column)
                g.insert(column,"[*] ")
            if row == "h":
                h.pop(column)
                h.insert(column,"[*] ")
        
            printGrid(nums,lines,a,b,c,d,e,f,g,h)

    cRowsList = ["a","b","c","d","e","f","g","h"]
    for i in range (5):
        alreadyEntered = True
        randRow = random.randint(0,7)
        randCol = random.randint(1,8)
        cRow = cRowsList[randRow]
        stringCol = str(randCol)
        COMcoordinate = cRow + stringCol
        while alreadyEntered == True:
            if COMcoordinate in COMenteredCoorindates:
                alreadyEntered = True
                randRow = random.randint(0,7)
                randCol = random.randint(1,8)
                cRow = cRowsList[randRow]
                stringCol = str(randCol)
                COMcoordinate = cRow + stringCol
            else:
                alreadyEntered = False
        if alreadyEntered == False:
            COMenteredCoorindates.append(COMcoordinate)
            if cRow == "a":
                COMa.pop(randCol)
                COMa.insert(randCol,"[*] ")
            if cRow == "b":
                COMb.pop(randCol)
                COMb.insert(randCol,"[*] ")
            if cRow == "c":
                COMc.pop(randCol)
                COMc.insert(randCol,"[*] ")
            if cRow == "d":
                COMd.pop(randCol)
                COMd.insert(randCol,"[*] ")
            if cRow == "e":
                COMe.pop(randCol)
                COMe.insert(randCol,"[*] ")
            if cRow == "f":
                COMf.pop(randCol)
                COMf.insert(randCol,"[*] ")
            if cRow == "g":
                COMg.pop(randCol)
                COMg.insert(randCol,"[*] ")
            if cRow == "h":
                COMh.pop(randCol)
                COMh.insert(randCol,"[*] ")

    print("All your coordinates and the computers coordinates have been selected. This is your target tracker:\n")
    printTargetTracker(TTnums,TTlines,TTa,TTb,TTc,TTd,TTe,TTf,TTg,TTh)
    print("You can use it to track where you have attacked and if the have hit or missed enemy boats.\n")
    
    gameWon = False
    printCOMGrid(COMnums,COMlines,COMa,COMb,COMc,COMd,COMe,COMf,COMg,COMh)
    
    while gameWon == False:
        #Player attack
        playerScore = 0
        comScore = 0
        attack = input("Enter the coordinates of where you want to attack:\n ")
        #Player hit computer
        if attack in COMenteredCoorindates:
            print("HIT!\n")
            row = attack[0]
            column = attack[1]
            column = int(column)
            if row == "a":
                TTa.pop(column)
                TTa.insert(column,"[H] ")
            if row == "b":
                TTb.pop(column)
                TTb.insert(column,"[H] ")
            if row == "c":
                TTc.pop(column)
                TTc.insert(column,"[H] ")
            if row == "d":
                TTd.pop(column)
                TTd.insert(column,"[H] ")
            if row == "e":
                TTe.pop(column)
                TTe.insert(column,"[H] ")
            if row == "f":
                TTf.pop(column)
                TTf.insert(column,"[H] ")
            if row == "g":
                TTg.pop(column)
                TTg.insert(column,"[H] ")
            if row == "h":
                TTh.pop(column)
                TTh.insert(column,"[H] ")
            printTargetTracker(TTnums,TTlines,TTa,TTb,TTc,TTd,TTe,TTf,TTg,TTh)
            playerScore = playerScore + 1
            if playerScore == 5:
                gameWon = True
        else:
            #Player miss computer
            print("MISS!\n")
            row = attack[0]
            column = attack[1]
            column = int(column)
            if row == "a":
                TTa.pop(column)
                TTa.insert(column,"[M] ")
            if row == "b":
                TTb.pop(column)
                TTb.insert(column,"[M] ")
            if row == "c":
                TTc.pop(column)
                TTc.insert(column,"[M] ")
            if row == "d":
                TTd.pop(column)
                TTd.insert(column,"[M] ")
            if row == "e":
                TTe.pop(column)
                TTe.insert(column,"[M] ")
            if row == "f":
                TTf.pop(column)
                TTf.insert(column,"[M] ")
            if row == "g":
                TTg.pop(column)
                TTg.insert(column,"[M] ")
            if row == "h":
                TTh.pop(column)
                TTh.insert(column,"[M] ")
            printTargetTracker(TTnums,TTlines,TTa,TTb,TTc,TTd,TTe,TTf,TTg,TTh)
        #Computer Attack
        time.sleep(3)
        alreadyEntered = True
        randRow = random.randint(0,7)
        randCol = random.randint(1,8)
        cRow = cRowsList[randRow]
        stringCol = str(randCol)
        COMcoordinate = cRow + stringCol
        while alreadyEntered == True:
            if COMcoordinate in COMattackCoordinates:
                alreadyEntered = True
                randRow = random.randint(0,7)
                randCol = random.randint(1,8)
                cRow = cRowsList[randRow]
                stringCol = str(randCol)
                COMcoordinate = randRow + randCol
            else:
                alreadyEntered = False
        if alreadyEntered == False:
            print("Computer attacks", COMcoordinate)
            COMattackCoordinates.append(COMcoordinate)
            #Computer hit player
            if COMcoordinate in enteredCoordinates:
                print(COMcoordinate,"\n")
                print("HIT!\n")
                row = COMcoordinate[0]
                column = COMcoordinate[1]
                column = int(column)
                if row == "a":
                    a.pop(column)
                    a.insert(column,"[H] ")
                if row == "b":
                    b.pop(column)
                    b.insert(column,"[H] ")
                if row == "c":
                    c.pop(column)
                    c.insert(column,"[H] ")
                if row == "d":
                    d.pop(column)
                    d.insert(column,"[H] ")
                if row == "e":
                    e.pop(column)
                    e.insert(column,"[H] ")
                if row == "f":
                    f.pop(column)
                    f.insert(column,"[H] ")
                if row == "g":
                    g.pop(column)
                    g.insert(column,"[H] ")
                if row == "h":
                    h.pop(column)
                    h.insert(column,"[H] ")
                printGrid(nums,lines,a,b,c,d,e,f,g,h)
            else:
                #Computer miss player
                print(COMcoordinate,"\n")
                print("MISS!\n")
                row = COMcoordinate[0]
                column = COMcoordinate[1]
                column = int(column)
                if row == "a":
                    a.pop(column)
                    a.insert(column,"[M] ")
                if row == "b":
                    b.pop(column)
                    b.insert(column,"[M] ")
                if row == "c":
                    c.pop(column)
                    c.insert(column,"[M] ")
                if row == "d":
                    d.pop(column)
                    d.insert(column,"[M] ")
                if row == "e":
                    e.pop(column)
                    e.insert(column,"[M] ")
                if row == "f":
                    f.pop(column)
                    f.insert(column,"[M] ")
                if row == "g":
                    g.pop(column)
                    g.insert(column,"[M] ")
                if row == "h":
                    h.pop(column)
                    h.insert(column,"[M] ")
                printGrid(nums,lines,a,b,c,d,e,f,g,h)






## Main program from here
if startMenuInput == "1":
    print("\nNew game")
    newGame()
if startMenuInput == "2":
    print("\nContinue game")
if startMenuInput == "3":
    sys.exit(0)
Reply
#2
(Aug-10-2021, 10:47 AM)DanielCook Wrote: This works a lot of the time but at some points instead of getting c5 it would give 25 and i'm not sure why as it doesn't do it all the time.
It is not serious to expect that we/anyone will look into 350 lines of code to debug an error that you get sparingly from time to time...
Even without error your code is in despread need of refactoring.
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#3
Your code would benefit greatly from a new representation of the grid.

A grid is a 2D array. An easy way to represent this in Python is to make a list of lists.
tracker_grid = [[' ']*8 for _ in range(8)]
This list comprehension makes 8 rows, each row being [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']

Coordinates are combinations of letters and numbers. 'a1' is the first row, first column and 'h8' the last row last column. You can convert a coordinate to the row and column using the ordinal values of the letters.
row = ord(coords[0])-ord('a')
col = ord(coords[1])-ord('1')
Now you can easily get or set the marker for any position in the grid with only a few lines of code. Since this will be done often, I would write a couple of functions.
def get(board, coords):
    '''Get marker on board at coords'''
    row = ord(coords[0])-ord('a')
    col = ord(coords[1])-ord('1')
    return board[row][col]

def set(board, coords, marker):
    '''Set marker on board at coords'''
    row = ord(coords[0])-ord('a')
    col = ord(coords[1])-ord('1')
    board[row][col] = marker
A big part of your game is getting input from the player. This is done at the start of the game to position the players boats, and then through the game to get the player's attack. Since this is done in two separate places it is a candidate for a function.
def enter_coords(prompt, board=None):
    '''Enter coordinates.  Verify coordinates are valid'''
    while True:
        coords = input(prompt).lower()
        if coords[0] in 'abcdefgh' and coords[1] in '12345678':
            if board is None or get(board, coords) == ' ':  # Check if coords already entered
                return coords
            print('Coordinates already entered.')
        else:
            print('Coordinates are rc where r is a letter a-g and c is a number 1-8')
Add a function that can print a grid, and you are ready to play a game. This code encapsulates the grid and a hit counter in a class.
import random
import time

EMPTY = ' '
BOAT  = 'B'
HIT   = '*'
MISS  = '-'

class Grid():
    '''8x8 playing field.  The grid starts empty.  The player and computer place BOATs at
    different coordinates.  Player and computer take turns launching attacks.  A successful
    attack is marked as a HIT.  The first to get 5 hits wins.
    '''
    def __init__(self, id=None):
        self.grid = [[EMPTY]*8 for _ in range(8)]
        self.hits = 0
        self.id = id

    def __getitem__(self, coords):
        '''Return grid[coords]'''
        return self.grid[ord(coords[0])-ord('a')][ord(coords[1])-ord('1')]

    def __setitem__(self, coords, value):
        '''Set grid[coords] = value'''
        self.grid[ord(coords[0])-ord('a')][ord(coords[1])-ord('1')] = value

    def attack(self, coords):
        '''Attack coords.  Update grid and hit counter.  Return 1 for hit, 0 for miss'''
        hit = 1 if self[coords] == BOAT else 0
        self.hits += hit
        self[coords] = (MISS, HIT)[hit]
        return hit

    def print(self, header=None, allowed=(BOAT, HIT, MISS)):
        '''Print grid.  Center header over grid.  Only print markers in the allowed list'''
        if header is not None:
            print('', f'{header:^35}', sep='\n')
        print("   1   2   3   4   5   6   7   8\n  -------------------------------")
        for label, row in zip('abcdefgh', self.grid):
            print(f'{label} ', ' | '.join([f'{x}' if x in allowed else f'{EMPTY}' for x in row]))

def enter_coords(prompt, grid, allowed=(EMPTY, BOAT)):
    '''Enter coordinates.  Verify coordinates are valid.  If grid is provided verify coordinates are empty'''
    while True:
        coords = input(prompt).lower()
        if len(coords) < 2 or coords[0] not in 'abcdefgh' or coords[1] not in '12345678':
            print('Coordinates are rc where r is a-g and c is 1-8')
        elif grid[coords] not in allowed:
            print('Coordinates already entered.')
        else:
            return coords

def new_game():
    '''Play a new game'''
    player = Grid()
    computer = Grid()
    remaining_coords = [f'{r}{c}' for r in 'abcdefgh' for c in '12345678']
    random.shuffle(remaining_coords)
    get_coords = []

    # Place the boats
    for coords in random.sample(remaining_coords, 5):
        computer[coords] = BOAT
        player.print('Enter position for a boat')
        player[enter_coords("Coordinates: ", player, (EMPTY,))] = BOAT

    while True:
        #Player attack
        computer.print('Enter attack coordinates', (MISS, HIT))
        coords = enter_coords("Coordinates: ", computer)
        result = ('MISS', 'HIT')[computer.attack(coords)]
        if computer.hits >= 5:
            computer.print(f'{coords} is a HIT.  Player WINS!')
            return
        computer.print(f'{coords} is a {result}', (MISS, HIT))
        time.sleep(3)

        #Computer Attack
        coords = remaining_coords.pop()
        result = ('MISS', 'HIT')[player.attack(coords)]
        if player.hits >= 5:
            player.print(f'Computer attacks {coords}.  Computer WINS!')
            return
        player.print(f'Computer attacks {coords}, {result}')
        time.sleep(3)

new_game()
Reply
#4
Your error is in this code that only gets called when the computer randomly selects a coordinate that was already used. Starting at line 260 in your code:
        while alreadyEntered == True:
            if COMcoordinate in COMattackCoordinates:
                alreadyEntered = True
                randRow = random.randint(0,7)
                randCol = random.randint(1,8)
                cRow = cRowsList[randRow]
                stringCol = str(randCol)
                COMcoordinate = randRow + randCol  # Should be cRow + stringCol
This code is used a couple times in your code making it a prime candidate for reuse. Putting frequently used code in a function reduces overall code, but more importantly reduces chances for errors like the one in your program.
def random_coord(used_coords):
    while True:
        coords = random.choice('abcdefgh') + random.choice('12345678')
        if coords not in (used_coords):
            used_coords.append(coords)
            return coords
This simple function reduces this:
        alreadyEntered = True
        randRow = random.randint(0,7)
        randCol = random.randint(1,8)
        cRow = cRowsList[randRow]
        stringCol = str(randCol)
        COMcoordinate = cRow + stringCol
        while alreadyEntered == True:
            if COMcoordinate in COMattackCoordinates:
                alreadyEntered = True
                randRow = random.randint(0,7)
                randCol = random.randint(1,8)
                cRow = cRowsList[randRow]
                stringCol = str(randCol)
                COMcoordinate = randRow + randCol
            else:
                alreadyEntered = False
        if alreadyEntered == False:
            print("Computer attacks", COMcoordinate)
            COMattackCoordinates.append(COMcoordinate)
to this:
        COMcoordinate = random_coords(COMattackCoordinates)
        print("Computer attacks", COMcoordinate)
and this
    cRowsList = ["a","b","c","d","e","f","g","h"]
    for i in range (5):
        alreadyEntered = True
        randRow = random.randint(0,7)
        randCol = random.randint(1,8)
        cRow = cRowsList[randRow]
        stringCol = str(randCol)
        COMcoordinate = cRow + stringCol
        while alreadyEntered == True:
            if COMcoordinate in COMenteredCoorindates:
                alreadyEntered = True
                randRow = random.randint(0,7)
                randCol = random.randint(1,8)
                cRow = cRowsList[randRow]
                stringCol = str(randCol)
                COMcoordinate = cRow + stringCol
            else:
                alreadyEntered = False
        if alreadyEntered == False:
            COMenteredCoorindates.append(COMcoordinate)
            if cRow == "a":
to
    for i in range (5):
        COMcoordinate = random_coords(COMenteredCoorindates)
        if cRow == "a":
 
Reply
#5
Your method for randomly choosing coordinates is very inefficient. Here's a little program that counts how long it takes to randomly select 64 different numbers in the range 1..64.
import random

for _ in range(10):
    used_spots = set()
    i = 0
    while len(used_spots) < 64:
        used_spots.add(random.randint(1, 64))
        i += 1
    print(i)
Output:
309 478 209 320 306 452 295 221 393 327
You do not put cards back in the deck while dealing a hand, so why not use the same kind of subtractive logic for coordinates. This code makes a "deck" of coordinates and shuffles the deck. To get a random coordinate you just deal the top card.
import random

random_coords = [r+c for r in 'abcdefgh' for c in '12345678']  # Make list of all coordinates
random.shuffle(random_coords)

for _ in range(5):
    print(random_coords.pop())  # Deal the top card
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
Bug TypeError: 'NoneType' object is not subscriptable TheLummen 4 679 Nov-27-2023, 11:34 AM
Last Post: TheLummen
  Help with python 'not subscriptable' error Extra 3 1,976 Dec-16-2022, 05:55 PM
Last Post: woooee
  TypeError: 'NoneType' object is not subscriptable syafiq14 3 5,166 Sep-19-2022, 02:43 PM
Last Post: Larz60+
  'int' object is not subscriptable after API call ed8484 1 1,765 Sep-18-2021, 02:06 PM
Last Post: ed8484
  Bool Object is not Subscriptable quest 1 4,086 May-02-2021, 11:12 AM
Last Post: Yoriz
  Float Object is not Subscriptable quest 2 2,853 Apr-20-2021, 09:28 AM
Last Post: quest
  AttributeError: 'Message' object has no attribute 'split' helpme1 2 4,932 Mar-14-2021, 11:25 AM
Last Post: helpme1
  TypeError: 'NoneType' object is not subscriptable Jmekubo 6 27,222 Sep-08-2020, 10:03 AM
Last Post: DigiTMG
  TypeError: 'type' object is not subscriptable Stef 1 4,440 Aug-28-2020, 03:01 PM
Last Post: Gribouillis
  'NoneType' object is not subscriptable Justchse 4 3,662 Aug-01-2020, 06:18 PM
Last Post: Justchse

Forum Jump:

User Panel Messages

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