# Four to One Game Server

The two player game four-to-one works by leaving four stones on a table. Each player may remove 2 or 1 stones from the table. The first player who cannot take away any stones loses. I created a web page that allows you to play this game against a computer that plays the game perfectly1.

Here is the code:

# server.py

import hug
import json
from hug.middleware import CORSMiddleware

# Probably should never do this but lol.
api = hug.API(__name__)

def do_move(pos, mov):
"""
Given a particular position perform a move in four to one.
Note: Assumes validity of move.
"""
return pos - mov

def gen_moves(pos):
"""
Generate every possible move given a position in the game four to one.
"""
if pos == 1:
return [1]
elif pos == 0:
return []
else:
return [1, 2]

# -------------------------------------------------------------------
# Collection of functions to update state of the best position found.
# old is the previous best state data, new is the challenger.
# -------------------------------------------------------------------

def loss_loss(old, new):
return new

def loss_win(old, new):
return old[0], max(old[1], new[1])

def win_loss(old, new):
return old[0], min(old[1], new[1])

def no_op(old, new):
return old

#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------

STATES = {
"LOSS" : { "LOSS" : loss_loss, "WIN" : loss_win },
"WIN"  : { "LOSS" : win_loss,  "WIN" : no_op },
}

@hug.get('/')
def next_best_move(pos: hug.types.number):
"""
Given a position, returns the next best move that someone should play
Arguments:
pos -- The current position of the board.
"""
moves = gen_moves(pos)
with open('fto1.json') as raw_data:
best = moves[0]
best_stats = [data[str(do_move(pos, moves[0]))]['value'],
data[str(do_move(pos, moves[0]))]['remoteness']]
for move in moves:
move_stats = (data[str(do_move(pos, move))]['value'],
data[str(do_move(pos, move))]['remoteness'])
old_best_stats = best_stats
best_stats = STATES[best_stats[0]][move_stats[0]](best_stats, move_stats)
if best_stats != old_best_stats:
best = move
return best


# logic.js

/**
* A representation of the game four to one that
*/
var game = new StateMachine({
init: 'playerTurn',
transitions: [
{ name: 'playerMove', from: 'playerTurn', to: 'computerTurn' },
{ name: 'computerMove', from: 'computerTurn', to: 'playerTurn' },
],

data: {
board: 4,
},

methods: {
onPlayerMove: function(lifecycle, amount) {
this.board -= amount;
if (this.board <= 0) {
$("#board span").text("Game over"); } else {$("#board span").text(this.board);
}
},
onComputerMove: function() {
self = this;
return $.ajax({ type: "GET", url: "http://127.0.0.1:8000/", dataType: 'json', data: {pos: self.board }, }).done(function (resp) { self.board -= resp; if (self.board <= 0) {$("#board span").text("Game over");
} else {
$("#board span").text(self.board); } }); }, }, });$('document').ready(function() {
/**
* Remove one "stone" from the board.
*/
$("#remove1").on('click', function() { game.playerMove(1); }); /** * Remove two "stones" from the board. */$("#remove2").click(function() {
game.playerMove(2);
});

/**
* Check for updates in the state, used to determine if the computer can m-
* ake a move.
*/
window.setInterval(function() {
game.computerMove();
}, 2000);
});


# index.html

<html>
<meta charset="UTF-8">
<script
src="http://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script
type="text/javascript"
src="state-machine.js"
></script>
<script
type="text/javascript"
src="logic.js"
></script>
<body>
<div id="board">
<span>4</span>
</div>
<div>
<button id="remove1">-1</button>
<button id="remove2">-2</button>
</div>
</body>
</html>


There is a github project here so feel free to view/clone/download that if you find that easier to work with.

I haven't worried about styling the game yet, so it looks kind of aesthetically displeasing.

1 Well, you start off in a winning position, so you can pretty much always beat the computer.

While you might be mostly interested in feedback on the Python code, I am still working on my Python skills. I can say that the use of storing the data in the JSON file and reading it looks good. Perhaps I can offer more feedback on the Python code in the future.

I can however offer feedback on the JavaScript.

### Code duplication

It appears that the code in the onPlayerMove() and onComputerMove() has some redundancy. The redundant code could be abstracted into a separate method like below:

updateBoardByAmount: function(amount) {
this.board -= amount;
if (this.board <= 0) {
$("#board span").text("Game over"); } else {$("#board span").text(this.board);
}
}


Then that method can be called when necessary:

onPlayerMove: function(lifecycle, amount) {
this.updateBoardByAmount(amount);
},
onComputerMove: function() {
messageContainer = $("#board span");  And then use that variable when necessary - for example: messageContainer.text("Game over");  ### jQuery DOM-loaded callback This is nothing to worry about, but the format of the jQuery DOM-loaded callback (i.e. $('document).ready(function() {) works but is deprecated1.

The recommended format is simply:

\$(function() {


• Thanks for the answer! I actually was looking for help with both. I am more familiar with Python the language, but less familiar with hug than I am with jQuery. I am going to hold off on accepting for a little bit to see if there are any other suggestions. Thanks again!
– Dair
Commented Nov 8, 2017 at 20:20
• Sure - I feel like one should wait (perhaps 1+ days) so as to allow other users that might potentially provide an answer to do so Commented Nov 8, 2017 at 21:06