Mongo Just Pawn in Game of Life

This is in response to this nifty blog post on storing a chess board in MySQL and this snarky Tweet about NoSQL DBs (because I’m never snarky).

On the one hand, I can’t believe I’m doing this. What database can’t store a chessboard? On the other hand, it’s fun, and once I thought of the title, I really had to write the post. Let the pointless data storage begin!

Okay, so first we need a representation for a chess piece. I’m tempted to just use the UTF-8 symbol and position, but it would be nice ot have a human-readable string to query on. So, we’ll use something like:

{
    "name" : "black king",
    "symbol" : "♚",
    "pos" : [8, "e"] 
}

Ha! Can your relational database query for a subfield of a subfield of type integer or string? (Actually, I have no idea, maybe it can.) Anyway, moving right along…

So, MongoDB can just run JavaScript, so I’ll write a JavaScript file that does everything we need. Here’s the code to create the basic chess board. “db” is a global variable that is the database you’re connected to. It defaults to “test”, so we’ll start by switching it to the “chess” database. If it doesn’t exist yet, it’ll be created when we put something in it. Then we’ll actually populate it:

// use the "chess" database, creates it if it doesn't exist
db = db.getSisterDB("chess");
// make sure the db is empty (in case we run this multiple times)
db.dropDatabase();

// map indexes to chess board locations
column_map = {0 : "a", 1 : "b", 2 : "c", 3 : "d", 4 : "e", 5 : "f", 6 : "g", 7 : "h"};
 
// starting at 1a
color_char = {"black" : "█", "white" : " "};
color = "black";
for (i=1; i<=8; i++) {
    for(j=0; j<8; j++) {
        db.board.insert({x : i, y : column_map[j], color : color_char[color]})
 
        /* 
         * switch the color of the square... it's always the opposite
         * of the previous one, unless we're at the end of a row
         */
        if (j != 7) {
            color = color == "white" ? "black" : "white";
        }
    }
}

Okay, now let’s iterate through the pieces, create their objects, and add them to the board:

// create unique ids from symbols
function get_name(symbol, column) {
    switch (symbol) {
    case '♖':
    case '♜':
        return " rook " + (column < 4 ? "left" : "right");
    case '♘':
    case '♞':
        return " knight " + (column < 4 ? "left" : "right");
    case '♗':
    case '♝':
        return " bishop " + (column < 4 ? "left" : "right");
    case '♕':
    case '♛':
        return " queen";
    case '♔':
    case '♚':
        return " king";
    case '♙':
    case '♟':
        return " pawn " + column;
    }
}


// go through the 2D array of pieces, create the objs, and insert them
function add_pieces(color, color_str) {
    for (row=0; row<color.length; row++) {
         chess_row = row + (color_str == "white" ? 1 : 7);

         for (column=0; column < color[row].length; column++) {
             chess_column = column_map[column];
 
             db.board.update({x : chess_row, y : chess_column}, {$set : {piece : {name : color_str+get_name(color[row][column], column), symbol : color[row][column], pos : [chess_row, chess_column]}}});
       }
    }
}

add_pieces([['♖','♘','♗','♕','♔','♗','♘','♖'], ['♙','♙','♙','♙','♙','♙','♙','♙']], "white");
add_pieces([['♟','♟','♟','♟','♟','♟','♟','♟'], ['♜','♞','♝','♛','♚','♝','♞','♜']], "black");

Phew! The hard part is done. Let’s print out this sucker!

// sort by x from 8-1 and y from a-h
cursor = db.board.find().sort({x:-1, y:1});


count = 0;
board = "";
while(cursor.hasNext()) {
    square = cursor.next();
    if (square.piece) {
        board += square.piece.symbol;
    }
    else {
        board += square.color;
    }

    count++;
    if (count % 8 == 0) {
        board += "n";
    }
}
print(board);

And we get:

♜♞♝♛♚♝♞♜
♟♟♟♟♟♟♟♟
 █ █ █ █
█ █ █ █ 
 █ █ █ █
█ █ █ █ 
♙♙♙♙♙♙♙♙
♖♘♗♕♔♗♘♖

Very snazzy. Now we can query by symbol, human readable name, or board position. Also, it’ll only take two updates to move a piece. (I attached chess.js, if you don’t want to copy/paste it yourself.)

11 thoughts on “Mongo Just Pawn in Game of Life

  1. You got the chessboard upside down (Black queen starts on black square) :p

    If I read this correctly, I think you should be able to do piece moves in one update — the board representation isn’t necessary to this code. So don’t nobody go away thinking you need 2 updates for a move, you just need 2 updates for the code to draw the board to be legible and concise 🙂

    The real question for me is how can we scale this to play some sort of crazy bughouse chess?
    1) can you write it to scale above a 64-square chessboard (yes, trivially!)
    2) what operations go into drawing the board (1 object per square on the board — pfeh!)
    3) what operations go into determining available moves (if not already done for drawing the board…)
    4) what operations go into a move?
    5) what operations go into a capture?

    Mongo can handle a much much larger chessboard, many more pieces, and many more players; you can shard by player, by piece, or by board-section; you can obviously do moves in one update if you store only pieces-with-location, because then you don’t need to erase the old piece’s location.
    You can also use this to query for pieces that you threaten (for rooks, bishops, and queens, you can use a where clause to find things along horizontals/verticals/diagonals; for the others, it works sorta similarly…), returning a cursor over all threatened pieces (maybe even ranked by piece value!).

    I’m not sure if there’s a way to perform a capture in one update — I feel like there must be, but without representing the whole board as a single object (which is _obviously_ cheating), I’m not sure what it is.

    Back to the plum-pudding mines for me. Happy holidays, all 🙂

    Like

  2. You got the chessboard upside down (Black queen starts on black square) :p

    If I read this correctly, I think you should be able to do piece moves in one update — the board representation isn’t necessary to this code. So don’t nobody go away thinking you need 2 updates for a move, you just need 2 updates for the code to draw the board to be legible and concise 🙂

    The real question for me is how can we scale this to play some sort of crazy bughouse chess?
    1) can you write it to scale above a 64-square chessboard (yes, trivially!)
    2) what operations go into drawing the board (1 object per square on the board — pfeh!)
    3) what operations go into determining available moves (if not already done for drawing the board…)
    4) what operations go into a move?
    5) what operations go into a capture?

    Mongo can handle a much much larger chessboard, many more pieces, and many more players; you can shard by player, by piece, or by board-section; you can obviously do moves in one update if you store only pieces-with-location, because then you don’t need to erase the old piece’s location.
    You can also use this to query for pieces that you threaten (for rooks, bishops, and queens, you can use a where clause to find things along horizontals/verticals/diagonals; for the others, it works sorta similarly…), returning a cursor over all threatened pieces (maybe even ranked by piece value!).

    I’m not sure if there’s a way to perform a capture in one update — I feel like there must be, but without representing the whole board as a single object (which is _obviously_ cheating), I’m not sure what it is.

    Back to the plum-pudding mines for me. Happy holidays, all 🙂

    Like

  3. @Andrew: whoops, thanks! I fixed the backwards chessboard and the position of the queens. Your other questions are left as an exercise for the reader 🙂

    So, who wants to build the world’s first MMOCPG (Massively Multiplayer Online Chess-Playing Game)?

    Like

  4. @Andrew: whoops, thanks! I fixed the backwards chessboard and the position of the queens. Your other questions are left as an exercise for the reader 🙂

    So, who wants to build the world’s first MMOCPG (Massively Multiplayer Online Chess-Playing Game)?

    Like

Leave a comment