patternjavascriptModerate
Python, JavaScript, PHP, HTML: Unbeatable Tic Tac Toe
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)
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
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
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.
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
Unused variables should be given the name
Why create a list comprehension?
When comparing to
That whole function can be combined into one line:
You never need to say
From PEP8:
Don't use spaces around the
I strongly recommend that you read that whole PEP.
To save processing time, you should add
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
What are
This can be simplified to:
or:
What if the user doesn't supply any arguments? You'd have a very embarrassing error. You should make sure that
You don't need to call the class method with an instance as an argument; just use it like an instance method:
Another hardcoded value. Who knows? You may want to switch to using
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.
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 NoneYou 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 Nonedef 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 = FalseTo save processing time, you should add
break.return NoneThat 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 4What 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.