1

I am a python beginner and am trying to make a simple game like Conway's game of life.

I am starting with a single game piece, the "walker" (represented by a 1 on the board), and am trying to get it to bounce left and right across the game board (which is an 8x8 grid kind of... more like a 1x64 grid, but I'm working on that)

# making the game board with an array
board = [0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0]

# note that game board (array) looks like this
# 0  1  2  3  4  5  6  7
# 8  9  10 11 12 13 14 15
# 16 17 18 19 20 21 22 23
# 24 25 26 27 28 29 30 31
# 32 33 34 35 36 37 38 39
# 40 41 42 43 44 45 46 47
# 48 49 50 51 52 53 54 55
# 56 57 58 59 60 61 62 63

My plan is to store the walker's position in the variable walkerPos, which is initially set to be somewhere on the edge.

By default I want the walker to move right, but if the walker reaches an edge, I want it to detect what edge it's on and bounce toward the other direction.

sideTiles = [0,1,2,3,4,5,6,7,8,16,24,32,40,48,56,57,58,59,60,61,62,63,55,47,39,31,23,15]
cornerTiles = [0,7,56,63]
sideTileChoice = random.randint(0,27)

global walkerPos
walkerPos = sideTiles[sideTileChoice]
print('Initial walker Position: ' + str(walkerPos))

So, I have it choose a random position along the edge, and then I start the game by calling the function startGame(), which looks like this:

def startGame(turn, walkerPos, gameOver):
    resetBoard() # makes all the places on the board 0
    initPlace() # places the walker on a random side edge
    print() # places a space in the console
    displayBoard() # shows the array values line by line in a board fashion

# now that the game is setup, we can enter the game loop until we finish the game
    while gameOver == 0:
        turn += 1 # set the gamestate to a new turn
        walkerMove(walkerPos, walkRight)
        print()
        displayBoard()
        time.sleep(2)
        print()

Like the game of life, the idea is that the game will play itself, so I have it go a turn at a time until it finishes. The code for the walker's movement is stored in the function walkerMove, which looks like this:

def walkerMove(walkerPos, walkRight):
    if (walkerPos % 8 == 0):
        print('walkRight=True')
        print()
        walkRight = True
    if (walkerPos + 1) % 8 == 0:
        print('walkRight=False')
        print()
        walkRight = False
    if walkRight: # if we are on the left edge...
        print(walkerPos)
        board[walkerPos] = 0 # remove our last position from the board
        walkerPos = walkerPos + 1 # change walkers position to the right
        board[walkerPos] = walker # place walker at one position to the right of last
        print('Walker moves right...')
    else:
        print(walkerPos)
        board[walkerPos] = 0 # remove our last position from the board
        walkerPos = walkerPos - 1 # change walkers position to the left
        board[walkerPos] = walker # place walker at one position to the left of last
        print('Walker moves left...')

I'm sure this is horrible code but my idea is to check if the walker has encountered an edge, a left one or a right one, and to change a variable that tells the walker what direction it should be moving in... Here's where my problem is. I had something like this working earlier by using walkerPos += 1, but at the time, the walkers position was being updated inside of startGame(), and I figured it would be better to have one function per game piece's moveset so I moved the code into a new function, walkerMove() and now the increments are all off.

The game starts properly:

Initial walker Position: 2
Welcome to 'It's Easier this Way'!
Game Board starts in the default state
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0

Press 'SPACE' to begin.

Then the user presses space, triggering startGame()...

0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
2
Walker moves right...

We see that the walker is indeed in the 2nd position, and the code correctly predicts that the walker must move right.

So here is the next iteration of the loop:

0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0

2
Walker moves right...

Notice that the walker DID move to the right like intended, but the walkerPos is still listed as 2, that's the number being printed right above 'Walker moves right...' But I am not sure how it ever got there, since as we can see, the walkerPos is still 2. Even weirder is the next iteration(s) of the loop (every iteration is the same from here on out, no matter the starting position of the walker)

0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0

2
Walker moves right...

This pattern happens every time. The game starts fine, the initial board placements are right. The game piece moves one space in whatever direction it's supposed to (even if it spawns on the right edge it will move left like intended, but only once before getting stuck), and the walkerPos variable seems like it's never changing even though the fact that the walker can move suggests otherwise.

I am sure I made a very silly mistake somewhere, but as a beginner, I am stuck trying to figure out what's going wrong. Can anyone lend a hand?

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • You must use a `global` statement if you are going to assign to a global variable inside a function... – thebjorn Jun 06 '22 at 17:44
  • Put `global walkerPos` inside `walkerMove`, and remove the `walkerPos` parameter from `walkerMove`. – Paul M. Jun 06 '22 at 17:46
  • @thebjorn even if I don't make the variable global I run into the same problems. I made them global to see if that would fix it, and it obviously did not go so great :'[ – Trevor XCIX Jun 06 '22 at 17:48
  • @Paul M. This fixed it. Thanks a bunch. Marking as answered – Trevor XCIX Jun 06 '22 at 17:50
  • Alternatively, return `walkerPos` from `walkerMove` and assign the returned value to `walkerPos` -`walkerPos = walkerMove(walkerPos, walkRight)` – wwii Jun 06 '22 at 18:23
  • Related: [Python: Keep changes on a variable made within a function](https://stackoverflow.com/questions/53249829/python-keep-changes-on-a-variable-made-within-a-function) – wwii Jun 06 '22 at 18:34

1 Answers1

-1

So I wanted to present a comprehensive answer to your question, since these sorts of NxM grid questions come up a lot. Also using a single index (rather than a 2D array) makes it an interesting question. (Others have already pointed out the global variable issues, so I wont cover these.) So here goes...

While there's nothing wrong with hard-coding the edge-indexes like you have, it's pretty easy to generalise this to determine edge positions on any sort of NxM rectangular area. Then you can easily have maps of any size, and the set of calculations is not particularly CPU-intensive (probably lighter than a 64-element search).

Assuming the WIDTH and HEIGHT are global variables, a series of simple tests can determine whether any given index is on an edge:

def indexIsOnEdge( index, edge ):
    """ Given an index, and a 'NESW' edge name, return true if the index is on the
        specified edge """
    global WIDTH, HEIGHT
    onEdge = False

    if ( edge == 'N' and index < WIDTH ):
        onEdge = True
    elif ( edge == 'E' and ( index + 1 ) % WIDTH == 0 ):
        onEdge = True
    elif ( edge == 'S' and index >= ( WIDTH * HEIGHT ) - WIDTH ):
        onEdge = True
    elif ( edge == 'W' and index % WIDTH == 0 ):
        onEdge = True

    return onEdge

Here we're just examining the index in conjunction with the board WIDTH and HEIGHT:

  • If the index is < WIDTH it must be in the first row.
  • If the modulus (remainder) of 1+index with the WIDTH is zero, it must be on the right-edge. It's 1+ because we start counting indexes at zero.
  • Similarly modulus of the index with WIDTH is zero, it must be on the left-edge
  • Leaving only the bottom-row, which is when the index is higher than the count of cells (WIDTHxHEIGHT-1) in all the previous rows.

Then to decide if it's OK to move, it's just a check of the current location with a desired direction of movement:

def walkerMove( walkerPos, direction ):
    """ Given a current position, and a 'NSEW' direction, move the walker
        returning the new-position,True, or the same-position,False if the movement
        was illegal (due to hitting an edge) """

    global WIDTH, HEIGHT
    moved = False

    if ( direction == 'N' and not indexIsOnEdge( walkerPos, 'N' ) ):
        walkerPos -= WIDTH  # up / north
        moved = True
    elif ( direction == 'E' and not indexIsOnEdge( walkerPos, 'E' ) ):
        walkerPos += 1      # east / right
        moved = True
    elif ( direction == 'S' and not indexIsOnEdge( walkerPos, 'S' ) ):
        walkerPos += WIDTH  # down / south
        moved = True
    elif ( direction == 'W' and not indexIsOnEdge( walkerPos, 'W' ) ):
        walkerPos -= 1      # left / west
        moved = True

    # return the possibly updated position
    return walkerPos, moved

This allows the coder to check:

newPosition, movedAtAll = walkerMove( currentPosition, 'E' )

And then if movedAtAll is True, we know to update the board.

This is easily extended to cover all movements:

#! /usr/bin/env python3
import random

# making the game board with an array
board = [0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0]


WIDTH=8
HEIGHT=8

def getEmptyBoard():
    """ Return a WIDTHxHEIGHT board of zeros """
    global WIDTH, HEIGHT
    #board = []
    #for i in range( WIDTH * HEIGHT ):
    #    board.append( 0 )
    board = [ 0 for x in range( WIDTH * HEIGHT ) ]
    return board


def indexIsOnEdge( index, edge ):
    """ Given an index, and a 'NESW' edge name, return true if the index is on the
        specified edge """
    global WIDTH, HEIGHT
    onEdge = False

    if ( edge == 'N' and index < WIDTH ):
        onEdge = True
    elif ( edge == 'E' and ( index + 1 ) % WIDTH == 0 ):
        onEdge = True
    elif ( edge == 'S' and index >= ( WIDTH * HEIGHT ) - WIDTH ):
        onEdge = True
    elif ( edge == 'W' and index % WIDTH == 0 ):
        onEdge = True

    return onEdge



def indexOnEdge( index, debug=False ):
    """ Return true if the given index would be an edge-position on a WIDTHxHEIGHT board """
    global WIDTH, HEIGHT
    onEdge = False

    if ( index < WIDTH ):  # top edge
        if ( debug ):
            print( "Index %d is top" % ( index ) )
        onEdge = True    
    elif ( index >= ( WIDTH * HEIGHT ) - WIDTH ):   # bottom edge
        if ( debug ):
            print( "Index %d is bottom" % ( index ) )
        onEdge = True     
    elif ( index % WIDTH == 0 ): # left side
        if ( debug ):
            print( "Index %d is left" % ( index ) )
        onEdge = True
    elif ( ( index + 1 ) % WIDTH == 0 ):  #right side
        if ( debug ):
            print( "Index %d is right" % ( index ) )
        onEdge = True
    elif ( debug ):
        print( "Index %d is inside" % ( index ) )

    return onEdge


def edgeAlgorithmTest( board ):
    """ Print the layout of the board to the console, for debugging """
    global WIDTH, HEIGHT

    for i in range( WIDTH*HEIGHT ):
        if ( indexOnEdge( i ) ):
            print( "#", end='' )
        else:
            print( ".", end='' )

        if ( (i+1) % ( WIDTH ) == 0 ):
            print( "" )


def printBoard( board ):
    """ Print the content of the board to the console """
    global WIDTH, HEIGHT

    for i in range( WIDTH*HEIGHT ):
        if (board[i] == 0 ):
            print( ".", end='' )   # empty
        if ( board[i] == 1 ):
            print( "o", end='' )   # a cell

        if ( (i+1) % ( WIDTH ) == 0 ):
            print( "" )


def walkerMove( walkerPos, direction ):
    """ Given a current position, and a 'NSEW' direction, move the walker
        returning the new-position,True, or the same-position,False if the movement
        was illegal (due to hitting an edge) """

    global WIDTH, HEIGHT
    moved = False

    if ( direction == 'N' and not indexIsOnEdge( walkerPos, 'N' ) ):
        walkerPos -= WIDTH  # up / north
        moved = True
    elif ( direction == 'E' and not indexIsOnEdge( walkerPos, 'E' ) ):
        walkerPos += 1      # east / right
        moved = True
    elif ( direction == 'S' and not indexIsOnEdge( walkerPos, 'S' ) ):
        walkerPos += WIDTH  # down / south
        moved = True
    elif ( direction == 'W' and not indexIsOnEdge( walkerPos, 'W' ) ):
        walkerPos -= 1      # left / west
        moved = True

    # return the possibly updated position
    return walkerPos, moved



# MAIN

board = getEmptyBoard()
walkerPos = 1
board[walkerPos] = 1  # first cell

# debug edge and print functions
#for i in range( WIDTH * HEIGHT ) :
#    indexOnEdge( i, True )
#edgeAlgorithmTest( b )

NORTH = 'N'
SOUTH = 'S'
EAST  = 'E'
WEST  = 'W'

printBoard( board )

for x in range( 500 ):   # just a short loop to test
    moveDir = random.choice( [ NORTH, SOUTH, EAST, WEST ] )

    print( "Try move [%s]" % ( moveDir ) )
    newPos, moved = walkerMove( walkerPos, moveDir )
    if ( moved ):
        board[walkerPos] = 0   # erase old position
        walkerPos = newPos
        board[walkerPos] = 1   # create new position
        printBoard( board )

Giving an output something like:

........
........
........
........
........
........
........
..o.....
Try move [S]   <-- REJECTED
Try move [W]   <-- OK
........
........
........
........
........
........
........
.o......

    
Kingsley
  • 14,398
  • 5
  • 31
  • 53