Skip to content

Instantly share code, notes, and snippets.

@niklasf
Last active December 16, 2015 21:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niklasf/5497933 to your computer and use it in GitHub Desktop.
Save niklasf/5497933 to your computer and use it in GitHub Desktop.
Tic-tac-toe over TCP.
var net = require('net');
var nextGame = null;
function Game() {
var self = this;
this.clients = new Array();
var board = new Array("1", "2", "3",
"4", "5", "6",
"7", "8", "9");
var turn = -1;
var moves = 0;
this.toString = function () {
return board[0] + " | " + board[1] + " | " + board[2] + "\r\n" +
"---------\r\n" +
board[3] + " | " + board[4] + " | " + board[5] + "\r\n" +
"---------\r\n" +
board[6] + " | " + board[7] + " | " + board[8] + "\r\n";
};
this.sendBoard = function () {
for (var i = 0; i < 2; i++) {
self.send(i, self.toString());
if (turn == i) {
self.send(i, "> \r\n");
} else {
self.send(i, "Waiting for a move ...\r\n");
}
}
};
this.start = function() {
for (var i = 0; i < 2; i++) {
(function(i) {
self.clients[i].on("data", function (data) {
if (turn == i) {
self.makeMove(i, data);
} else {
self.send(i, "Not your turn yet ...\r\n");
}
});
self.clients[i].on("end", function (data) {
self.end("Your opponent forfeits.\r\n");
});
self.clients[i].on("error", function () {
self.end("Your opponent forfeits.\r\n");
});
})(i);
}
turn = 0;
self.sendBoard();
}
this.makeMove = function(i, data) {
field = parseInt(data, 10);
if (field >= 1 && field <= 9 &&
board[field - 1] != "X" && board[field - 1] != "O")
{
if (turn == 0) {
board[field - 1] = "X";
turn = 1;
} else {
board[field - 1] = "O";
turn = 0;
}
self.sendBoard();
if (self.wins("X") || self.wins("O")) {
if (self.wins("X")) {
self.end("X wins.\r\n");
} else {
self.end("O wins.\r\n");
}
turn = -1;
} else if (++moves >= 9) {
self.end("Draw.\r\n");
turn = -1;
}
} else {
self.send(i, "Pick an empty field between 1 and 9.\r\n");
self.send(i, "> \r\n");
}
};
this.wins = function(s) {
return ((board[0] == s && board[1] == s && board[2] == s) ||
(board[3] == s && board[4] == s && board[5] == s) ||
(board[6] == s && board[7] == s && board[8] == s) ||
(board[0] == s && board[3] == s && board[6] == s) ||
(board[1] == s && board[4] == s && board[7] == s) ||
(board[2] == s && board[5] == s && board[8] == s) ||
(board[0] == s && board[4] == s && board[8] == s) ||
(board[2] == s && board[4] == s && board[6] == s));
};
this.send = function(i, message) {
try {
self.clients[i].write(message);
} catch (e) { }
};
this.end = function(message) {
for (var i = 0; i < 2; i++) {
try {
self.clients[i].end(message);
} catch (e) { }
}
};
}
var server = net.createServer(function (socket) {
if (nextGame) {
nextGame.clients[1] = socket;
nextGame.start();
nextGame = null;
} else {
nextGame = new Game();
nextGame.clients[0] = socket;
nextGame.send(0, "Waiting for an opponent ...\r\n");
}
});
var port = parseInt(process.argv[2], 10) || 8888;
server.listen(port);
console.log("Listening on port " + port + " ...");
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtNetwork import *
import sys
import re
class ConnectionDialog(QDialog):
def __init__(self, parent):
super(ConnectionDialog, self).__init__(parent)
layout = QGridLayout()
layout.addWidget(QLabel("Host:"), 0, 0)
self.hostBox = QLineEdit()
self.hostBox.setText("localhost")
layout.addWidget(self.hostBox, 0, 1)
layout.addWidget(QLabel("Port:"), 1, 0)
self.portBox = QSpinBox()
self.portBox.setMinimum(1)
self.portBox.setMaximum(60000)
self.portBox.setValue(8888)
layout.addWidget(self.portBox, 1, 1)
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons, 2, 1)
self.setLayout(layout)
self.setWindowTitle("Tic-tac-toe: New game")
class TicTacToeButton(QPushButton):
def __init__(self):
super(TicTacToeButton, self).__init__()
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
sizePolicy.setHeightForWidth(True)
self.setSizePolicy(sizePolicy)
font = self.font()
font.setPixelSize(50)
self.setFont(font)
def heightForWidth(self, width):
return width
def sizeHint(self):
return QSize(100, 100)
def setText(self, text):
palette = self.palette()
if text == "X":
palette.setColor(QPalette.ButtonText, Qt.red)
self.setStyleSheet("color: red;")
elif text == "O":
palette.setColor(QPalette.ButtonText, Qt.blue)
self.setStyleSheet("color: blue;")
self.setPalette(palette)
super(TicTacToeButton, self).setText(text)
class GameWindow(QMainWindow):
def __init__(self):
super(GameWindow, self).__init__()
self.socket = None
self.rowRegex = re.compile(r"^([0-9XO])\s\|\s([0-9XO])\s\|\s([0-9XO])$")
vbox = QVBoxLayout()
self.statusLabel = QLabel("Connecting ...")
self.statusLabel.setAlignment(Qt.AlignCenter)
vbox.addWidget(self.statusLabel, Qt.AlignCenter)
layout = QGridLayout()
self.buttons = QButtonGroup()
self.buttons.setExclusive(False)
self.buttons.buttonClicked.connect(self.buttonClicked)
for x in range(0, 3):
for y in range(0, 3):
button = TicTacToeButton()
layout.addWidget(button, y, x)
self.buttons.addButton(button, x + y * 3)
vbox.addLayout(layout)
self.setCentralWidget(QWidget())
self.centralWidget().setLayout(vbox)
self.setWindowTitle("Tic-tac-toe")
self.newGameAction = QAction("New", self)
self.newGameAction.setShortcut("Ctrl+N")
self.newGameAction.triggered.connect(self.onNewGame)
self.closeAction = QAction("Close", self)
self.closeAction.triggered.connect(self.close)
menu = self.menuBar().addMenu("Game")
menu.addAction(self.newGameAction)
menu.addAction(self.closeAction)
def onNewGame(self):
connectionDialog = ConnectionDialog(self)
if connectionDialog.exec_():
self.row = 0
if self.socket:
self.socket.close()
self.socket = QTcpSocket(self)
self.socket.readyRead.connect(self.onReadyRead)
self.socket.error.connect(self.onError)
self.socket.connectToHost(connectionDialog.hostBox.text(), connectionDialog.portBox.value())
def onReadyRead(self):
while self.socket.canReadLine():
line = str(self.socket.readLine()).strip()
print line
if line == ">":
self.statusLabel.setText("Make your move ...")
elif line == "---------":
pass
else:
matches = self.rowRegex.match(line)
if matches:
for x in range(0, 3):
if matches.group(x + 1) in ("X", "O"):
self.buttons.button(x + self.row * 3).setText(matches.group(x + 1))
else:
self.buttons.button(x + self.row * 3).setText("")
self.row = (self.row + 1) % 3
else:
self.statusLabel.setText(line)
def onError(self, error):
if error == QAbstractSocket.RemoteHostClosedError:
self.statusLabel.setText(self.statusLabel.text() + " Connection closed.")
elif error == QAbstractSocket.HostNotFoundError:
self.statusLabel.setText("Host not found.")
elif error == QAbstractSocket.ConnectionRefusedError:
self.statusLabel.setText("Connection refused.")
else:
self.statusLabel.setText("Error: %s." % self.socket.errorString())
def buttonClicked(self, button):
self.socket.write("%d\n" % (self.buttons.id(button) + 1))
if __name__ == "__main__":
app = QApplication(sys.argv)
game_window = GameWindow()
game_window.show()
game_window.onNewGame()
app.exec_()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment