HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

Python, JavaScript, PHP, HTML: Unbeatable Tic Tac Toe

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
unbeatablejavascriptphptoetictacpythonhtml

Problem

At first, I created an unbeatable Tic-Tac-Toe game in Python. However, I wanted a better interface, so I used a PHP backend that runs the script and simple JavaScript that sends user commands to the script.

index.html: (excuse stupid class names)


  
  
  
  
  
   Tic-Tac-Toe Unbeatable 
  
  table {
    text-align: center;
    vertical-align: middle;
    font: bold 36px sans-serif;
}
p,
h1,
td,
th {
    font-family: 'Lato', sans-serif;
}
table,
td,
th {
    border: none;
    border-collapse: collapse;
  }
    td, th {
        text-align: center;
        font-weight: 300;
        height: 150px;
        width: 150px;
        font-size: 1em;
    }
    #board tr td:hover {
        background: #e4e4e4;
        cursor: pointer;
    }
    .ab_c {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    td#c0,
    td#c1,
    td#c2,
    td#c3,
    td#c4,
    td#c5 {
        border-bottom: 3px solid grey;
    }
    td#c0,
    td#c1,
    td#c3,
    td#c4,
    td#c6,
    td#c7 {
        border-right: 3px solid grey;
    }
    .lolyoucantbeatme {
        color: grey;
        font-weight: 400;
        font-size: 4em;
    }
    .lolyoureallywontbutyoucantry {
        color: grey;
        font-weight: 100;
        font-size: 2em;
    }
  

  
  
  
     Tic Tac Toe 
     Try to beat me! 
  
  
  
    
      
    
    
      
    
    
      
    
  
  


ttt-realhard.js:

```
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
ttt = {
"ticclick": function(cell) {
if ($(cell).html() == '' && !ttt.system.laptopresponse) { $(cell).html('X'); ttt.system.lock();ttt.system.freecode();ttt.system.help(); }
// TODO: Wait for computer move.
},
"realz": [
'#c0','#c1','#c2',
'#c3','#c4','#c5',
'#c6','#c7','#c8'
],
}
ttt['system'] = {
"freecode": function() {
for (var i = 0; i logic.length) {$('#lawl').hide();return;}
$('#lawl').show().htm

Solution

As JosephtheDreamer has left the Python to someone else, I'll take up the challenge.

It's a small thing, but the styling for the definition of winning_combos doesn't seem right to me. We can see the start of the definition and what is inside, but the close parenthesis is hidden at the end of one of the lines. I prefer to put it on its own line:

winning_combos = (
    [0, 1, 2], [3, 4, 5], [6, 7, 8],
    [0, 3, 6], [1, 4, 7], [2, 5, 8],
    [0, 4, 8], [2, 4, 6],
)


That way, it's more clear what is inside of the tuple without actually looking through it. I also like to have a trailing comma so that it isn't as easy to add another list and get a SyntaxError for forgetting it.

def __init__(self, square=[]):


It is dangerous to use a mutable object as a default argument. In this case it doesn't matter because you don't use it anyway. As long as you aren't using it, you should use None instead. That way, nothing needs to be created (None has already been defined, but an empty list needs to be created.) When using None, use if square is None: instead of if len(square) == 0:. While we're on that, you could have said if not square: instead of if len(square) == 0:

self.squares = [None for i in range(9)]


Unused variables should be given the name _ to make it clear that they are unused. You don't even need a list comprehension here, though. Just say self.squares = [None] * 9. List comprehensions are useful in similar cases if the object is mutable and you want to instantiate a new one for each element in the list, but None is immutable so that is unnecessary in this case.

if None not in [v for v in self.squares]:


Why create a list comprehension? self.squares is already a list, so you can do if None not in self.squares:

if self.winner() != None:


When comparing to None, you should usually use is None and is not None. If you intend to compare with any Falsey value, you should be using if self.winner(): instead.

That whole function can be combined into one line:

return None not in self.squares or self.winner() is not None:


return self.complete() == True and self.winner() is None


You never need to say == True because if it is equal to True, it already has a boolean value. Just say return self.complete() and self.winner() is None

def winner(self, lastcheck = False):


From PEP8:


Don't use spaces around the = sign when used to indicate a keyword argument or a default parameter value.

I strongly recommend that you read that whole PEP.

if pos not in positions:
    win = False


To save processing time, you should add break.

return None


That is a completely useless line when given at the end of a function definition. When no return statement is given, the function will return None by default. Even if you were returning at an earlier time than the function would otherwise, you can say return without the None. Edit: As Darkhogg has mentioned in the comments, it isn't entirely useless here. It does improve the clarity of what happens when there is no winner.

if len(board.available_moves()) == 9:
    return 4


What are 9 and 4? This should at least have comments, but even better don't use hard-coded values. Instead, define some constants at the beginning of the file. That makes it easier to change your program later.

def get_enemy(player):
    if player == 'X':
        return 'O'
    return 'X'


This can be simplified to:

def get_enemy(player):
    return ('O', 'X')[player == 'X']


or:

def get_enemy(player):
    return 'O' if player == 'X' else 'X'


a = sys.argv[1]


What if the user doesn't supply any arguments? You'd have a very embarrassing error. You should make sure that sys.argv has the right length before just taking right out of it.

a = str.upper(a)


You don't need to call the class method with an instance as an argument; just use it like an instance method: a = a.upper()

if v == '-':


Another hardcoded value. Who knows? You may want to switch to using * later on. Put a constant at the beginning of the file for your empty-space-filler and use that instead.

if board.complete():
    dat['Board']['Winner'] = board.winner()
    dat['Board']['WinningCombo'] = board.lastwincombo;
    ...


That code is in your program twice. Duplicate code that is more than about three lines (in my opinion) should be in its own function.

Code Snippets

winning_combos = (
    [0, 1, 2], [3, 4, 5], [6, 7, 8],
    [0, 3, 6], [1, 4, 7], [2, 5, 8],
    [0, 4, 8], [2, 4, 6],
)
def __init__(self, square=[]):
self.squares = [None for i in range(9)]
if None not in [v for v in self.squares]:
if self.winner() != None:

Context

StackExchange Code Review Q#123337, answer score: 12

Revisions (0)

No revisions yet.