0

As an attempt to pick up the basics of python I decided to try and recreate nim sticks in python 3. I'm using pyCharm for this development.

When the AI player checks for what sticks to remove the lines variable in the game_ai function appears to overwrite itself for no apparent reason.

main.py:

import ai
import helpers

lines = [1, 3, 5, 7]
winner = 0

# String introduction
print('   _____ _   _      _         ')
print('  / ____| | (_)    | |        ')
print(' | (___ | |_ _  ___| | _____  ')
print('  \___ \| __| |/ __| |/ / __| ')
print('  ____) | |_| | (__|   <\__ \ ')
print(' |_____/ \__|_|\___|_|\_\___/ ')
print('The last one to pick up a stick loses!')

# Keep going till we have a winner!
while winner == 0:
    # Redraw the stick board at the start of a turn
    helpers.redraw_sticks(lines)

    # Get user input and remove their sticks
    line = helpers.check_input('Please select a line to remove (a) stick(s) from:', 0, 4, 'line', lines)
    amount = helpers.check_input('Please select how many stick(s) you wish to remove:', 0, 7, 'amount', lines)
    lines = helpers.remove_sticks(lines, line, amount)

    # Check for winner
    winner = helpers.winner_check(lines, 1)

    # Only play the AI if the player doesn't win this go
    if winner == 0:
        # Redraw the board after a go
        helpers.redraw_sticks(lines)

        # Play the AI
        ai_turn = ai.game_ai(lines)
        lines = helpers.remove_sticks(lines, ai_turn[0], ai_turn[1] - 1)

        # Check for winner
        winner = helpers.winner_check(lines, 2)

# Declare the winner!
if winner == 1:
    print('The Human Player wins the game!')
else:
    print('The Computer Player wins the game!')

ai.py:

import helpers
import time


# Our cool kid AI
def game_ai(lines):
    winning_data = [0, 0]
    winning_combo = False

    # Keep going till we get a winning nim sum
    while not winning_combo:
        line = 1

        # Go through every line
        while line <= 4 and not winning_combo:
            # Reset amount value
            amount = 7

            # Make it look like there's actual heavy processing going on here
            print('AI player is thinking...')
            time.sleep(1)

            # Only go if the line actually has sticks
            if lines[line - 1] != 0:
                # Test certain values in a line
                while amount >= 1 and not winning_combo:
                    lines_test = helpers.remove_sticks(lines, line, amount)

                    # If we have a nim sum of 0 we go!
                    if helpers.nim_sum(lines_test) == 0:
                        winning_combo = True
                        winning_data[0] = line
                        winning_data[1] = amount

                        # I'm going to win, I shouldn't do this...
                        if helpers.lines_checksum(lines_test) == 0:
                            winning_data[1] = amount - 1

                    # Increment amount
                    amount -= 1

            # Increment line
            line += 1

    # Return winning data
    return winning_data

helpers.py:

import sys


# Redraws the stick board with updated values
def redraw_sticks(lines):
    # Loop through each line
    for (line, sticks) in enumerate(lines):
        if sticks > 0:
            # Output line number
            sys.stdout.write(str(line + 1) + ': ')

            # And output how many sticks there are
            for x in range(0, sticks):
                sys.stdout.write('|')
            print('')


# Remove sticks on request
def remove_sticks(lines, line, amount):
    # Set line to the correct pointer
    line -= 1

    if lines[line] < amount:
        lines[line] = 0
    else:
        lines[line] = lines[line] - amount

    return lines


# Check if an input is an integer and in the correct range
def check_input(request_message, min_value, max_value, check_type, lines):
    inputting = True
    user_input = 0

    while inputting:
        user_input = input(request_message)

        try:
            # Check if the input is an integer
            user_input = int(user_input)

            # Make sure the input fits in our defined range
            if max_value >= user_input > min_value:
                inputting = False

                # Check if the line has any sticks left
                if check_type == 'line' and lines[user_input - 1] <= 0:
                    print('Error: Line is already empty')
                    inputting = True

            else:
                print('Error: Invalid range value entered')
        except ValueError:
            print('Error: String input entered')

    # Return the input after checks
    return user_input


def nim_sum(lines):
    # First we need to calculate the multiples
    nim_sum_total = 0

    for (pointer, sticks) in enumerate(lines):
        # Calculate multiples of 4
        nim_sum_total += sticks // 4
        sticks %= 4

        # Calculate multiples of 2
        nim_sum_total += sticks // 2
        sticks %= 2

        # Calculate multiples of 1
        nim_sum_total += sticks // 1

    return nim_sum_total % 2


# Check how many sticks are left
def lines_checksum(lines):
    checksum = 0

    # Calculate remaining sticks
    for sticks in lines:
        checksum += sticks

    return checksum


# Check if a winner has been found
def winner_check(lines, player):
    checksum = lines_checksum(lines)

    # Return if we have a winner
    if checksum == 1:
        return player
    else:
        return 0
user-33324
  • 17
  • 3
  • 1
    What do you mean with seems to overwrite itself? Can you be more specific? – DallaRosa Feb 18 '15 at 16:26
  • I duped you to a canonical post on how Python passes variables around. Your `remove_sticks()` function alters the list *in place*, meaning that all other references to the list also see that change. If you wanted to create a list copy, see [How to clone or copy a list in Python?](http://stackoverflow.com/q/2612802) – Martijn Pieters Feb 18 '15 at 16:27
  • @MartijnPieters this looks like the issue. In other languages that I've used this isn't the expected behaviour. Thanks! – user-33324 Feb 18 '15 at 16:33

1 Answers1

0

Everything in Python is passed by reference, so the lines variable in game_ai refers to the same list as the lines variable in your main program. Thus when you remove sticks from it, you end up clearing the shared lines variable.

To resolve this, you can make a copy of the lines first in game_ai, as lines = lines.copy().

nneonneo
  • 171,345
  • 36
  • 312
  • 383