2

I'm making a noughts and crosses game and I'm looking to see if a combination is valid.

I have a list of winning combinations e.g [0, 1, 2]

How would I check if 'X' is in all three, here's what I got so far:

def draw_grid():
  print("-------------")
  print('|', grid[0], '|', grid[1], '|', grid[2], '|')
  print("-------------")
  print('|', grid[3], '|', grid[4], '|', grid[5], '|')
  print("-------------")
  print('|', grid[6], '|', grid[7], '|', grid[8], '|')
  print("-------------")

Symbol = 'X'

def Checker():
  Winning = [[0, 1, 2]]

  #statement to check...



global grid
grid = ['X', 'X', 'X', 3, 4, 5, 6, 7, 8]

draw_grid()
Checker()
Ruthless
  • 35
  • 4
  • 2
    I can't help you atm, but it would be easier if you used `grid[x][y]` instead of the grid number, e.g. grid=[['X','X','X'],[0,0,0],[0,0,0]] – Jachdich Jun 09 '18 at 21:19
  • I had that before but someone recommended using one list rather than looping around nested lists. – Ruthless Jun 09 '18 at 21:23
  • @Ruthless It makes absolutely no difference in python. – cs95 Jun 09 '18 at 21:25
  • I'll see if I can work something out – Jachdich Jun 09 '18 at 21:25
  • @coldspeed I know what you're trying to say but I just prefer this way. – Ruthless Jun 09 '18 at 21:26
  • @Ruthless Using one list to avoid looping will probably actually make it more difficult in the long run, since the semantics of parsing and iterating through 3 in a row in a noughts and crosses board are better oriented towards 2 dimensions. – Graham Jun 09 '18 at 21:27
  • or you could simply write yourself a parsing function: `def coord(x,y): return y*3+x` to flip between both ways of speccing a coord. Its a representational choice - neither 1dim or 2dim are better for this simple game (ymho) – Patrick Artner Jun 09 '18 at 21:42

3 Answers3

1

You can do it like this:

def Checker(g,what):
    """Checks if any index combination inside Winner has all X"""
    Winning = [[0, 1, 2], [0,3,6], ] # you need to add all other 6 wind conditions here
    return any(  all( g[a]==what for a in x) for x in Winning)

win =  ['X', 'X', 'X', 3, 4, 5, 6, 7, 8]
win2 = ['X', 2, 3, 'X', 4, 5, 'X', 7, 8]
loose = ['X', 'X', 'o', 3, 4, 5, 6, 7, 8]

print (win, Checker(win,'X'))
print (win2, Checker(win2,'X'))
print (loose, Checker(loose,'X'))

Output:

['X', 'X', 'X', 3, 4, 5, 6, 7, 8] True
['X', 2, 3, 'X', 4, 5, 'X', 7, 8] True
['X', 'X', 'o', 3, 4, 5, 6, 7, 8] False
  • all() checks if the test if valid for all elements of an iterable
  • any() checks if any element of an iterable statisfies a condition

Example to understand any()/all()

t = [2,4,6]

print( all( x % 2 == 0 for x in t) )  # are all elements of t even?
print( any( x // 3 == 2 for x in t) ) # is any element of t divided by 3 == 2 ?
print( any( x % 2 == 1 for x in t) ) # is any element in t odd?

Output:

True
True
False

The line

return any(  all( g[a]==what for a in x) for x in Winning)

simply checks if any element of your Winning (either [0,1,2] or [0,3,6] - 6 more conditions to be added by yourself) has all grid-indexes g[..] (as given in this condition) with a value given by what - so you can check for X or O - either one might win.

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • Would you mind explaining 'return any( all( g[a]=="X" for a in x) for x in Winning)'? – Ruthless Jun 09 '18 at 21:30
  • This doesn't work for diagonals, [(Try it online link)](https://tio.run/##bVFBboMwELzzilF6CEhW1UCbVJU49Qk9NBLiYIIBq9SObKOQ19O1aUmpalnW7M7s7No@X12nVTZNtWjw2onThzBxyy4dd8lLBFqbzSbkLWQDrq6QqhYjTvqzkoo7qRVlrKwF3qVSwqDjFrzvcaTK4ODzUrXIURQPDDuGtGQgmLE9gTKIjHCDUb5BDF8eoy14med@EjTagFMbjEnAo8fftkkUXSjKgWJ73DLcjozhkeGJYc9wYHguvTD1YwQ6DYoAZ1mAs7DX2opFOR/6X8u7t04PfY1K0ITkz1ANjm7TSuuEoZegjV5be@@7Z4vnbjXA4khRGUVnI5VDHOx@PsUHxCbJLzZd0emaD3e4Cebwr0O2cshmfpq@AA "TIO link"), like `win3 = ['X', 1, 2, 3, 'X', 5, 6, 7, 'X']`. – Graham Jun 09 '18 at 21:39
  • @Graham - Provide the win condition inside `Winning` and it will - currently only 2 axis are defined - try to debug the code to see what I mean. `Winning = [[0, 1, 2], [0,3,6], [0,4,8],]` .. there are more conditions to be specified - thats not what the question was about so I only added one to the one the OP provided – Patrick Artner Jun 09 '18 at 21:42
  • Oh, I see. I misread the code as more sophisticated because I didn't understand the any() and all() construction... and it just clicked! – Graham Jun 09 '18 at 21:46
  • I've tried adding this to my code however there is an issue, it won't detect the correct result. Here is my repl https://repl.it/@awais/Noughts-and-Crosses-V2 – Ruthless Jun 09 '18 at 22:33
1

I don't know weather this is what you are wanting, but here's something that might work:

def draw_grid():
  print("-------------")
  print('|', grid[0], '|', grid[1], '|', grid[2], '|')
  print("-------------")
  print('|', grid[3], '|', grid[4], '|', grid[5], '|')
  print("-------------")
  print('|', grid[6], '|', grid[7], '|', grid[8], '|')
  print("-------------")

Symbol = 'X'

def Checker():
  Winning = [[0, 1, 2]]
  if grid[winning[0]] == Symbol and grid[winning[1]] == Symbol and grid[winning[2]] == Symbol:
      return "Match"



global grid
grid = ['X', 'X', 'X', 3, 4, 5, 6, 7, 8]

draw_grid()
Checker()

Hope this helped.

Jachdich
  • 782
  • 8
  • 23
  • This does work but as @Patrick Artner says I would have to do it with all 8 solutions. Meaning a lot of extra code. Thanks anyway – Ruthless Jun 09 '18 at 21:32
  • yep, that's why its kinda bad, but I'm bad at python. See Patrick Artner's answer for something better. – Jachdich Jun 09 '18 at 21:32
0

Let me suggest an object-oriented solution. Since what you want to implement is a nested list with additional functionality, we can implement a Grid class.

In particular, its method iter_winning_pos will return a generator for all the legal winning positions. It then suffices to pick your favorite way to check if all elements in a list are identical.

Grid class

class Grid():
    def __init__(self, n=3):
        self._grid = [[None] * n for _ in range(n)]

    def __getitem__(self, item):
        x, y = item
        return self._grid[x][y]

    def __setitem__(self, key, value):
        x, y = key
        self._grid[x][y] = value

    def __str__(self):
        s = ''
        for row in self._grid:
            s += '-' * (2 * len(self._grid) + 1) + '\n'
            s += ('|{}|\n'.format('|'.join([x if x is not None else ' ' for x in row])))
        s += '-' * (2 * len(self._grid) + 1) + '\n'
        return s

    def iter_winning_pos(self):
        for i in range(len(self._grid)):
            yield [(i, j) for j in range(len(self._grid))]
            yield [(j, i) for j in range(len(self._grid))]

        yield [(i, i) for i in range(len(self._grid))]
        yield [(i, len(self._grid) - 1 - i) for i in range(len(self._grid))]


    def is_winning(self):
        for line in self.iter_winning_pos():
            first = self[line[0]]
            if first is not None and all(self[x] == first for x in line):
                return first
        return None

Example

g = Grid()
print('{} is winning'.format(g.is_winning()))

g[0, 0] = 'X'
g[1, 1] = 'O'
g[0, 1] = 'X'
g[0, 2] = 'X'

print(g)
print('{} is winning'.format(g.is_winning()))

Output

None is winning
-------
|X|X|X|
-------
| |O| |
-------
| | | |
-------

X is winning
Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73