Skip to content

Instantly share code, notes, and snippets.

@tgdn
Created May 22, 2015 14:25
Show Gist options
  • Save tgdn/4d7311182ff0f6cfe6a6 to your computer and use it in GitHub Desktop.
Save tgdn/4d7311182ff0f6cfe6a6 to your computer and use it in GitHub Desktop.
Gomoku JS
section
.controls
.newGameCtrl
input(type="checkbox" id="check")
label.newMain(for="check")
.newMainText new
.newContainer
.newPlate
.boardCellCircle(id="new-O")
.newPlate
.boardCellCross(id="new-X")
.sizeCtrl
.newMain
.newMainText(id="scale-Up") +
.sizeCtrl
.newMain
.newMainText(id="scale-Down") -
.messages
.messagesContainer
.newMainText(id="message") try to get 5 in a row!
.board
- for (var x = 0; x < 15; x++)
.boardRow
- for (var y = 0; y < 15; y++)
.boardCol
.boardCell(id=x + "-" + y)
/**
* browserify, combines 3 modules
*/
(function e(t, n, r) {
function s(o, u) {
if (!n[o]) {
if (!t[o]) {
var a = typeof require == "function" && require;
if (!u && a) return a(o, !0);
if (i) return i(o, !0);
var f = new Error("Cannot find module '" + o + "'");
throw f.code = "MODULE_NOT_FOUND", f
}
var l = n[o] = {
exports: {}
};
t[o][0].call(l.exports, function(e) {
var n = t[o][1][e];
return s(n ? n : e)
}, l, l.exports, e, t, n, r)
}
return n[o].exports
}
var i = typeof require == "function" && require;
for (var o = 0; o < r.length; o++) s(r[o]);
return s
})({
/**
* browserify ends, modules:
*/
1: [function(require, module, exports) {
module.exports = function() {
var win = [
[1, 1, 1, 1, 1]
];
var unCovered4 = [
[0, 1, 1, 1, 1, 0]
];
var unCovered3 = [
[0, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 0],
[0, 1, 0, 1, 1, 0],
[0, 1, 1, 0, 1, 0]
];
var unCovered2 = [
[0, 0, 1, 1, 0, 0],
[0, 1, 0, 1, 0, 0],
[0, 0, 1, 0, 1, 0],
[0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0],
[0, 1, 0, 0, 1, 0]
];
var covered4 = [
[-1, 1, 0, 1, 1, 1],
[-1, 1, 1, 0, 1, 1],
[-1, 1, 1, 1, 0, 1],
[-1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, -1],
[1, 0, 1, 1, 1, -1],
[1, 1, 0, 1, 1, -1],
[1, 1, 1, 0, 1, -1]
];
var covered3 = [
[-1, 1, 1, 1, 0, 0],
[-1, 1, 1, 0, 1, 0],
[-1, 1, 0, 1, 1, 0],
[0, 0, 1, 1, 1, -1],
[0, 1, 0, 1, 1, -1],
[0, 1, 1, 0, 1, -1],
[-1, 1, 0, 1, 0, 1, -1],
[-1, 0, 1, 1, 1, 0, -1],
[-1, 1, 1, 0, 0, 1, -1],
[-1, 1, 0, 0, 1, 1, -1]
];
(function() { //add same combinations for another player
var allCombos = [win, unCovered4, unCovered3, unCovered2, covered4, covered3];
for (var k = 0; k < allCombos.length; k++) {
var temp = [];
for (var j = 0; j < allCombos[k].length; j++) {
var tmp = [];
for (var i = 0; i < allCombos[k][j].length; i++)
tmp[i] = -allCombos[k][j][i];
temp.push(tmp);
}
for (var m = 0; m < temp.length; m++) {
allCombos[k].push(temp[m]);
}
}
}());
var valueCombo = function(w, u2, u3, u4, c3, c4) {
if (w > 0) return 1000000000;
if (u4 > 0) return 100000000;
if (c4 > 1) return 10000000;
if (u3 > 0 && c4 > 0) return 1000000;
if (u3 > 1) return 100000;
if (u3 == 1) {
if (u2 == 3) return 40000;
if (u2 == 2) return 38000;
if (u2 == 1) return 35000;
return 3450;
}
if (c4 == 1) {
if (u2 == 3) return 4500;
if (u2 == 2) return 4200;
if (u2 == 1) return 4100;
return 4050;
}
if (c3 == 1) {
if (u2 == 3) return 3400;
if (u2 == 2) return 3300;
if (u2 == 1) return 3100;
}
if (c3 == 2) {
if (u2 == 2) return 3000;
if (u2 == 1) return 2900;
}
if (c3 == 3) {
if (u2 == 1) return 2800;
}
if (u2 == 4) return 2700;
if (u2 == 3) return 2500;
if (u2 == 2) return 2000;
if (u2 == 1) return 1000;
return 0;
};
var findArray = function(arr, inArr) {
var fCount = arr.length;
var sCount = inArr.length;
var k;
for (var i = 0; i <= fCount - sCount; i++) {
k = 0;
for (var j = 0; j < sCount; j++) {
if (arr[i + j] == inArr[j]) k++;
else break;
}
if (k == sCount) return true;
}
return false;
};
var isAnyInArrays = function(combos, arr) {
for (var i = 0; i < combos.length; i++) {
if (findArray(arr, combos[i])) return true;
}
return false;
};
var combinations = {};
combinations.winValue = 1000000000;
combinations.valuePosition = function(arr1, arr2, arr3, arr4) { // 4 directions
var w = 0,
u2 = 0,
u3 = 0,
u4 = 0,
c3 = 0,
c4 = 0;
var allArr = [arr1, arr2, arr3, arr4];
for (var i = 0; i < allArr.length; i++) {
if (isAnyInArrays(win, allArr[i])) {
w++;
continue;
}
if (isAnyInArrays(covered4, allArr[i])) {
c4++;
continue;
}
if (isAnyInArrays(covered3, allArr[i])) {
c3++;
continue;
}
if (isAnyInArrays(unCovered4, allArr[i])) {
u4++;
continue;
}
if (isAnyInArrays(unCovered3, allArr[i])) {
u3++;
continue;
}
if (isAnyInArrays(unCovered2, allArr[i])) {
u2++;
}
}
return valueCombo(w, u2, u3, u4, c3, c4);
};
return combinations;
};
}, {}],
2: [function(require, module, exports) {
Array.matrix = function(m, n, initial) {
var a, i, j, mat = [];
for (i = 0; i < m; i++) {
a = [];
for (j = 0; j < n; j++) {
a[j] = initial;
}
mat[i] = a;
}
return mat;
};
var initCombinations = require('./combinations');
module.exports = function(player) {
var gameSize = 5; // 5 in line
var ring = 1; // ring size around current cells
var win = false;
var cellsCount = 15;
var curState = Array.matrix(15, 15, 0);
var complexity = 1;
var maxPlayer = player || -1; // X = 1, O = -1
var combinations = initCombinations();
if (maxPlayer === -1) curState[7][7] = 1;
var checkWin = function() {
for (var i = 0; i < cellsCount; i++) {
for (var j = 0; j < cellsCount; j++) {
if (curState[i][j] == 0) continue;
var playerVal = combinations.valuePosition(
getCombo(curState, curState[i][j], i, j, 1, 0),
getCombo(curState, curState[i][j], i, j, 0, 1),
getCombo(curState, curState[i][j], i, j, 1, 1),
getCombo(curState, curState[i][j], i, j, 1, -1)
);
if (playerVal === combinations.winValue) {
win = true;
}
}
}
};
var miniMax = function minimax(node, depth, player, parent) {
if (depth == 0) return heuristic(node, parent);
var alpha = Number.MIN_VALUE;
var childs = getChilds(node, player);
for (var i = 0; i < childs.length; i++) {
alpha = Math.max(alpha, -minimax(childs[i], depth - 1, -player, node));
}
return alpha;
};
var isAllSatisfy = function(candidates, pointX, pointY) {
var counter = 0;
for (var i = 0; i < candidates.length; i++) {
if (pointX != candidates[i][0] || pointY != candidates[i][1]) counter++;
}
return counter == candidates.length;
};
var getChilds = function(parent, player) {
var children = [];
var candidates = [];
for (var i = 0; i < cellsCount; i++) {
for (var j = 0; j < cellsCount; j++) {
if (parent[i][j] != 0) {
for (var k = i - ring; k <= i + ring; k++) {
for (var l = j - ring; l <= j + ring; l++) {
if (k >= 0 && l >= 0 && k < cellsCount && l < cellsCount) {
if (parent[k][l] == 0) {
var curPoint = [k, l];
var flag = isAllSatisfy(candidates, curPoint[0], curPoint[1]);
if (flag) candidates.push(curPoint);
}
}
}
}
}
}
}
for (var f = 0; f < candidates.length; f++) {
var tmp = Array.matrix(cellsCount, cellsCount, 0);
for (var m = 0; m < cellsCount; m++) {
for (var n = 0; n < cellsCount; n++) {
tmp[m][n] = parent[m][n];
}
}
tmp[candidates[f][0]][candidates[f][1]] = -player;
children.push(tmp);
}
return children;
};
var getCombo = function(node, curPlayer, i, j, dx, dy) {
var combo = [curPlayer];
for (var m = 1; m < gameSize; m++) {
var nextX1 = i - dx * m;
var nextY1 = j - dy * m;
if (nextX1 >= cellsCount || nextY1 >= cellsCount || nextX1 < 0 || nextY1 < 0) break;
var next1 = node[nextX1][nextY1];
if (node[nextX1][nextY1] == -curPlayer) {
combo.unshift(next1);
break;
}
combo.unshift(next1);
}
for (var k = 1; k < gameSize; k++) {
var nextX = i + dx * k;
var nextY = j + dy * k;
if (nextX >= cellsCount || nextY >= cellsCount || nextX < 0 || nextY < 0) break;
var next = node[nextX][nextY];
if (next == -curPlayer) {
combo.push(next);
break;
}
combo.push(next);
}
return combo;
};
var heuristic = function(newNode, oldNode) {
for (var i = 0; i < cellsCount; i++) {
for (var j = 0; j < cellsCount; j++) {
if (newNode[i][j] != oldNode[i][j]) {
var curCell = newNode[i][j];
var playerVal = combinations.valuePosition(
getCombo(newNode, curCell, i, j, 1, 0),
getCombo(newNode, curCell, i, j, 0, 1),
getCombo(newNode, curCell, i, j, 1, 1),
getCombo(newNode, curCell, i, j, 1, -1)
);
newNode[i][j] = -curCell;
var oppositeVal = combinations.valuePosition(
getCombo(newNode, -curCell, i, j, 1, 0),
getCombo(newNode, -curCell, i, j, 0, 1),
getCombo(newNode, -curCell, i, j, 1, 1),
getCombo(newNode, -curCell, i, j, 1, -1)
);
newNode[i][j] = -curCell;
return 2 * playerVal + oppositeVal;
}
}
}
return 0;
};
var getLogic = {};
getLogic.winState = "";
getLogic.makeAnswer = function(x, y) {
var that = this;
curState[x][y] = maxPlayer;
checkWin();
if (win) {
that.winState = "you win";
return "";
}
var answ = [-1, -1];
var c = getChilds(curState, maxPlayer);
var maxChild = -1;
var maxValue = Number.MIN_VALUE;
for (var k = 0; k < c.length; k++) {
var curValue = miniMax(c[k], 0, -maxPlayer, curState);
if (complexity > 1) {
//var curValue2 = miniMax(c[k], complexity - 1, -maxPlayer, curState);
//use it for more complex game!
}
if (maxValue < curValue) {
maxValue = curValue;
maxChild = k;
}
}
for (var i = 0; i < cellsCount; i++) {
for (var j = 0; j < cellsCount; j++) {
if (c[maxChild][i][j] != curState[i][j]) {
answ[0] = i;
answ[1] = j;
curState[answ[0]][answ[1]] = -maxPlayer;
checkWin();
if (win) {
that.winState = "you lost";
}
return answ;
}
}
}
return answ;
};
return getLogic;
};
}, {
"./combinations": 1
}],
3: [function(require, module, exports) {
$(document).ready(function() {
var initLogic = require('./gomoku/logic');
var logic = initLogic();
$("#7-7").addClass("boardCellCross");
var currValue = -1; // player - O, computer - X
var gameOver = false;
$('div.boardCol').mousedown(handleMouseDown);
function handleMouseDown(e) {
if (gameOver) return "";
var cell = $(this);
if (cell.children().hasClass("boardCellCircle")) return "";
if (cell.children().hasClass("boardCellCross")) return "";
var indexes = (cell.children().attr('id')).split("-");
var answer = logic.makeAnswer(indexes[0], indexes[1]);
if (answer !== "") {
var getedId = '#' + answer[0] + '-' + answer[1];
$(getedId).addClass(deserve());
} else currValue *= -1;
cell.children().addClass(deserve());
function deserve() {
currValue *= -1;
if (currValue === 1) {
return "boardCellCross";
}
return "boardCellCircle";
}
if (logic.winState !== "") {
var message = $("#message");
message.text(logic.winState);
gameOver = true;
message.removeClass("looseState");
if (logic.winState === "you lost") {
message.addClass("looseState");
}
}
}
$("#scale-Up").click(handleScale);
$("#scale-Down").click(handleScale);
function handleScale(e) {
var value = 100;
var minValue = 300;
var delta = $(this).attr('id').split("-")[1];
var board = $(".board");
var controls = $(".controls");
if (delta === "Up") {
board.width(board.width() + value);
board.height(board.height() + value);
controls.width(controls.width() + value);
controls.height(controls.height() + value / 15);
}
if (delta === "Down" && board.width() > minValue) {
board.width(board.width() - value);
board.height(board.height() - value);
controls.width(controls.width() - value);
controls.height(controls.height() - value / 15);
}
}
$("#new-O").parent().click(handleNewGame);
$("#new-X").parent().click(handleNewGame);
function handleNewGame(e) {
var index = ($(this).children().attr('id')).split("-")[1];
$(".boardCell").removeClass("boardCellCross boardCellCircle");
gameOver = false;
$("#message").text("");
if (index === "O") {
logic = initLogic();
$("#7-7").addClass("boardCellCross");
currValue = -1;
}
if (index === "X") {
logic = initLogic(1);
currValue = 1;
}
$("#check").prop('checked', false);
}
});
}, {
"./gomoku/logic": 2
}]
}, {}, [3])
@import "bourbon";
@import url(http://fonts.googleapis.com/css?family=Roboto:700);
$FlatWetAsphalt : #34495e;
$FlatAlizarin : #e74c3c;
$FlatMidnightBlue : #2c3e50;
$FlatEmerald : #2ecc71;
$FlatClouds: #ecf0f1;
$mainFont: 'Roboto', sans-serif;
$boardWidth: 400px;
$boardSize: 15;
$boardElementWidth: 3px;
body {
margin: 0;
padding: 0;
background-color: $FlatMidnightBlue;
min-width: 300px;
}
section {
margin-top: 10px;
}
.board {
margin: auto;
margin-top: 10px;
width: $boardWidth;
height: $boardWidth;
}
.boardRow {
width: 100%;
height: 100% / $boardSize;
border-bottom: 1px solid $FlatMidnightBlue;
box-sizing: border-box;
}
.boardCol {
$columnWidth : 100%;
width: $columnWidth / $boardSize;
height: 100%;
background-color: $FlatWetAsphalt;
float: left;
border-radius: 3px;
border-left: 1px solid $FlatMidnightBlue;
box-sizing: border-box;
}
.boardCellCross {
$cellWidth : 70% * 1.41;
margin: auto;
margin-top: (100% - $cellWidth) / 2;
width: $boardElementWidth;
height: $cellWidth;
background-color: $FlatEmerald;
transform: rotate(-45deg);
border-radius: $boardElementWidth / 2;
&:before {
background-color: inherit;
border-radius: inherit;
height: inherit;
width: inherit;
position: absolute;
content: "";
transform: rotate(90deg);
}
}
.boardCellCircle {
$cellWidth : 70%;
margin: (100% - $cellWidth) / 2;
width: $cellWidth;
height: $cellWidth;
border: $boardElementWidth solid $FlatAlizarin;
border-radius: 50%;
box-sizing: border-box;
}
.controls {
width: $boardWidth;
height: $boardWidth / $boardSize;
margin: auto;
}
.newGameCtrl, .sizeCtrl, .messages {
position: relative;
display: inline-block;
height: 100%;
width: 100% / 15 * 1;
}
.sizeCtrl,.messages {
float:right;
}
.messages {
width: 100% /15 *11;
z-index: 0;
}
#check {
display: none;
}
.newContainer {
z-index: 10;
}
.newMain, .sizeScale, .messagesContainer {
width: 100%;
height: 100%;
position: absolute;
z-index: 50;
display: table;
text-align: center;
}
.newMain, .sizeScale {
cursor: pointer;
background-color: $FlatEmerald;
border-radius: 50%;
&:hover {
background-color: $FlatAlizarin;
}
}
.newMainText {
font-family: $mainFont;
font-size: 13px;
color: $FlatMidnightBlue;
display: table-cell;
vertical-align: middle;
}
.messagesContainer .newMainText {
color: $FlatEmerald;
}
.messagesContainer .looseState {
color: $FlatAlizarin;
}
.newPlate {
width: 100%;
height: 100%;
opacity: 0;
@include transition(all 0.2s ease-in);
@include position(absolute, 0 0 0 0);
z-index: 5;
&:hover {
background-color: $FlatWetAsphalt;
}
}
$base: 0;
@for $i from 1 through 2 {
.newPlate:nth-of-type(#{$i}) {
@include transition-delay(#{$base + s});
}
$base: $base + 0.3;
}
#check:checked ~ .newMain + .newContainer {
.newPlate {
opacity: 1;
}
.newPlate:nth-of-type(1) {
left: 100%;
}
.newPlate:nth-of-type(2) {
left: 200%;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment