1

So for now I have written a simple Tic tac toe game. I haven't even used all the functions I made but here is my question: How can I determine if the player places a 1 or 2 where there already is one, I think I know how to do this, but how can I then put them back at the "Input your number" prompt if they enter an illegal character or they try to overwrite an already placed 1 or 2.

Also is there a more compact way to do this?

Here is the code for the game:

nr = [0,0,0,0,0,0,0,0,0]
keepGoing = True

def checkP1():
    if nr[0] and nr[1] and nr[2] or nr[3] and nr[4] and nr[5] or nr[6] and nr[7] and nr[8] or nr[0] and nr[3] and \
       nr[6] or nr[1] and nr[4] and nr[7] or nr[2] and nr[5] and nr[8] or nr[0] and nr[4] and nr[8] or nr[2] and nr[4] and nr[6] == 1:
        print("P1 Wins")
        keepGoing = False
        return keepGoing



def checkP2():
    if nr[0] and nr[1] and nr[2] or nr[3] and nr[4] and nr[5] or nr[6] and nr[7] and nr[8] or nr[0] and nr[3] and \
       nr[6] or nr[1] and nr[4] and nr[7] or nr[2] and nr[5] and nr[8] or nr[0] and nr[4] and nr[8] or nr[2] and nr[4] and nr[6] == 2:
        print("P2 Wins")
        keepGoing = False
        return keepGoing

def Game():
    while keepGoing:
        PrintBoard()
        in1 = 0
        in2 = 0
        in1 = input("Please enter the number of the position you want to put your symbol P1.")
        nr[int(in1)-1] = 1
        check = checkP1()
        if check == 0:
            PrintBoard()
            break
        in2 = input("Please enter the number of the position you want to put your symbol P2.")
        check = checkP2()
        if check == 0:
            PrintBoard()
            break
        nr[int(in2)-1] = 2

def PrintBoard():
    print("",nr[0],nr[1],nr[2],"\n",nr[3],nr[4],nr[5],"\n",nr[6],nr[7],nr[8])

def Reset():
    nr = [0,0,0,0,0,0,0,0,0]

    keepGoing = True
Czarek
  • 597
  • 1
  • 10
  • 20
  • You should at least use `all()` to test for a winner, your `if` clauses are a mess. column Y: `all(nr[Y::3])`, row X: `all(nr[X:X+3])`, diagonals: `all(nr[::4])` and `all(nr[2:8:2])` – CodeManX Apr 25 '14 at 12:10
  • @CoDEmanX I forgot to state I am beginner, I don't really know a lot yet. I was trying what I can do with my current resources. – Czarek Apr 25 '14 at 12:29
  • @CoDEmanX I actually stopped learning for a while. Now when I look at this I get your point. I know that lists can take 2 arguments like list[x:y] but what happens if you give them 3 arguments like you did? list[x:y:z] What does it mean? – Czarek May 27 '14 at 13:58
  • The third argument is the step value, so you can read [2:8:2] as "start at 2, count up to 8 (exclusive) in steps of 2". `list(range(9))[2:8:2]` or `[0, 1, 2, 3, 4, 5, 6, 7, 8][2:8:2]` returns `[2, 4, 6]`. `[::4]` means "return every 4th element", which is `[0, 4, 8]` here. https://docs.python.org/3.5/library/functions.html#slice – CodeManX May 27 '14 at 14:28
  • @CoDEmanX I get it, the thing is I don't see a way that I could use this to make it more compact. I understand the way that it should work I just don't see a way to do it in code. It would be awesome if you could rewrite my check statements and show me what they should look like. – Czarek May 27 '14 at 16:40

2 Answers2

1

To answer your specific question

how can I then put them back at the "Input your number" prompt if they enter an illegal character or they try to overwrite an already placed 1 or 2

I would make a function to do this:

def get_valid_input(board):
    while True:
        try:
            move = int(input("Please enter the number of the position you want to put your symbol."))
        except ValueError:
            print("Input must be an integer number.")
        else:
            if move not in range(1, 10):
                print("Move must be 1-9.")
            elif board[move-1] in (1, 2):
                print("Location already used.")
            else:
                return move

This will continue until whichever player is currently going gives a valid move:

in1 = get_valid_input(nr)

A few more general pointers:

  • Your current checking for a winner doesn't work - if a or b == c doesn't do what you think it does (see e.g. this question). For example, nr[0] and nr[1] and nr[2] == 2 actually tests bool(nr[0]) and bool(nr[1]) and (nr[2] == 2); as long as the last value is two and the other two aren't zero, it will be True. CoDEmanX's suggestions in the comments will be useful here.
  • Using the flag keepgoing isn't very Pythonic; I would make a function game_over(board) that returns True if the game is finished (either a win or a draw) and False otherwise, then the overall loop becomes while True: ... if game_over(board): break.
  • Rather than relying on scope to access variables, pass what you need explicitly (e.g. the board argument to get_valid_input and game_over). Getting rid of keepgoing removes one of your global variables, but you could take the board as an argument to you other functions and return as appropriate. Alternatively, consider making a class to hold the board and all the functions.
Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • I really don't understand your 2 first bulleted points. I don't know if that is bad but I am just a beginner in python. As your code makes complete sense I don't really understand what the text below it means. – Czarek Apr 26 '14 at 06:09
  • I have added more to the comments. Have you read the linked question? If you don't understand the terms, try a web search for them. – jonrsharpe Apr 26 '14 at 07:01
0

An example of how slices can be used to determine the winner:

def same(iterable):
    it = iter(iterable)
    start_val = it.__next__()
    for val in it:
        if val != start_val:
            return
    if start_val != 0:
        return start_val

nr = [0] * 9

nr[1] = 1
nr[2] = 2
nr[4] = 2
nr[6] = 2
nr[7] = 1
nr[8] = 1

for i in range(0, len(nr), 3):
    print(nr[i:i+3])

winner = (
    same(nr[0:3]) or same(nr[3:6]) or same(nr[6:9]) or
    same(nr[0::3]) or same(nr[1::3]) or same(nr[2::3]) or
    same(nr[::4]) or same(nr[2:-1:2])
)

if winner is not None:
    print("Player %i won!" % winner)

same() is a helper function that checks if all elements of an iterable have the same value, and if so, return that value unless it is 0 (so we only catch 1s and 2s by players).

You see the patterns in the slice notations, they could be easily scaled by using for-loops.

CodeManX
  • 11,159
  • 5
  • 49
  • 70