Skip to content

Instantly share code, notes, and snippets.

@adrianseeley
Last active December 13, 2015 23:29
Show Gist options
  • Save adrianseeley/4992008 to your computer and use it in GitHub Desktop.
Save adrianseeley/4992008 to your computer and use it in GitHub Desktop.
Light Cycles in NodeJS! Plus Naive Neural Network (:
var GAME_WIDTH = 10;
var GAME_HEIGHT = 10;
var EMPTY = ' ';
var FULL = 'O';
function cls() { process.stdout.write('\u001B[2J\u001B[0;0f'); }
function replace (string, index, character) { return string.substr(0, index) + character + string.substr(index + character.length); }
function get_new_game(players)
{
var board = ''; for(var h = 0; h < GAME_HEIGHT; h++) for(var w = 0; w < GAME_WIDTH; w++) if(w == 0 || w == GAME_WIDTH - 1 || h == 0 || h == GAME_HEIGHT - 1) board += FULL; else board += EMPTY;
if(players >= 1) board = board.substr(0, GAME_WIDTH + 1) + '1' + board.substr(GAME_WIDTH + 1 + 1);
if(players >= 2) board = board.substr(0, board.length - GAME_WIDTH - 2) + '2' + board.substr(board.length - GAME_WIDTH - 2 + 1);
return { board: board, frame: 0 };
}
function draw_game(game) { cls(); for(var h = 0; h < GAME_HEIGHT; h++) console.log(game.board.substr(h * GAME_WIDTH, GAME_WIDTH)); }
function do_frame(game, done_callback)
{
var player_locations = [];
var player_at_location = [];
var player_moves = [];
// grab players, replace their positions with FULLs
for(var p = 0; p < game.board.length; p++) if(game.board[p] != EMPTY && game.board[p] != FULL) { player_locations.push(p); player_at_location.push(game.board[p]); game.board = replace(game.board, p, FULL); }
// if we have at least 2 players, process moves
if(player_locations.length > 1)
{
for(var p = 0; p < player_locations.length; p++)
{
var board_for_player = game.board;
for(var b = 0; b < player_locations.length; b++)
{
if(p == b) board_for_player = replace(board_for_player, player_locations[b], '+');
else board_for_player = replace(board_for_player, player_locations[b], '-');
}
player_moves.push(get_move(board_for_player, player_at_location[p]));
}
for(var m = 0; m < player_moves.length; m++)
{
switch(player_moves[m])
{
case 'n': if(game.board[player_locations[m] - GAME_WIDTH] == EMPTY) game.board = replace(game.board, player_locations[m] - GAME_WIDTH, player_at_location[m]); else if(game.board[player_locations[m] - GAME_WIDTH] != EMPTY && game.board[player_locations[m] - GAME_WIDTH] != FULL) replace(game.board, player_locations[m] - GAME_WIDTH, FULL); break;
case 's': if(game.board[player_locations[m] + GAME_WIDTH] == EMPTY) game.board = replace(game.board, player_locations[m] + GAME_WIDTH, player_at_location[m]); else if(game.board[player_locations[m] + GAME_WIDTH] != EMPTY && game.board[player_locations[m] + GAME_WIDTH] != FULL) replace(game.board, player_locations[m] + GAME_WIDTH, FULL); break;
case 'e': if(game.board[player_locations[m] + 1] == EMPTY) game.board = replace(game.board, player_locations[m] + 1, player_at_location[m]); else if(game.board[player_locations[m] + 1] != EMPTY && game.board[player_locations[m] + 1] != FULL) replace(game.board, player_locations[m] + 1, FULL); break;
case 'w': if(game.board[player_locations[m] - 1] == EMPTY) game.board = replace(game.board, player_locations[m] - 1, player_at_location[m]); else if(game.board[player_locations[m] - 1] != EMPTY && game.board[player_locations[m] - 1] != FULL) replace(game.board, player_locations[m] - 1, FULL); break;
}
}
game.frame++;
draw_game(game);
setTimeout(function () { done_callback(game, do_frame); }, 1000);
}
// if we have only 1 player, they are the winner
else if(player_locations.length == 1)
{
console.log('winner: ' + player_at_location[0]);
aether_end_game(parseInt(player_at_location[0]) - 1);
do_frame(get_new_game(2), do_frame);
}
// otherwise the last two players died on the same frame, its a draw
else
{
console.log('draw :/');
aether_end_game(-1);
do_frame(get_new_game(2), do_frame);
}
}
function get_move(board, player)
{
if(player == '1') return aether_get_move(board, 1 - 1);
if(player == '2') return aether_get_move(board, 2 - 1);
}
var aether = {};
var trail = [[], []];
function aether_get_move(board, player)
{
var awareness = aether_build_awareness(board);
if(aether[awareness])
{
var highest = 'n';
var at = aether[awareness].n;
if(aether[awareness].s > at) { highest = 's'; at = aether[awareness].s; }
if(aether[awareness].e > at) { highest = 'e'; at = aether[awareness].e; }
if(aether[awareness].w > at) { highest = 'w'; at = aether[awareness].w; }
trail[player].push({awareness: awareness, move: highest});
return highest;
}
else
{
var options = ['n', 's', 'e', 'w'];
var random = options[Math.floor(Math.random() * options.length)];
trail[player].push({awareness: awareness, move: random});
return random;
}
}
function aether_build_awareness(board)
{
var awareness = '';
var rows = [];
var my_x, my_r;
for(var r = 0; r < GAME_HEIGHT; r++) rows.push(board.substr(r * GAME_WIDTH, GAME_WIDTH));
out_of_nested_for: for(var r = 0; r < rows.length; r++) for(var x = 0; x < rows[r].length; x++) if(rows[r][x] == '+') { my_x = x; my_r = r; break out_of_nested_for; }
//build 5 x 5 awareness
for(var r = my_r - 3; r < my_r + 2; r++)
{
for(var x = my_x - 3; x < my_x + 2; x++)
{
if(r < 0 || r >= GAME_HEIGHT || x < 0 || x >= GAME_WIDTH || (x == my_x && r == my_r)) awareness += FULL;
else awareness += rows[r][x];
}
}
console.log(awareness);
return awareness;
}
function aether_end_game(winner)
{
for(var p = 0; p < trail.length; p++)
{
for(var t = 0; t < trail[p].length; t++)
{
if(!aether[trail[p][t].awareness]) aether[trail[p][t].awareness] = {n: -1, s: -1, e: -1, w: -1};
if(p == winner) aether[trail[p][t].awareness] [trail[p][t].move]++;
else aether[trail[p][t].awareness] [trail[p][t].move]--;
}
}
delete trail; trail = [[], []];
return;
}
do_frame(get_new_game(2), do_frame);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment