
 // Michael McElroy
 // michaelmcelroy.net, 2010

    var mm_game = function ()
    
        {
        this . wm = "6D69636861656C406D69636861656C6D63656C726F792E6E6574";
        this . board = null;
        this . pieces = null;
        this . moves = null;
        this . tape = null;
        this . settled = null;
        this . winner = null;
        this . self = null;
        this . opponent = null;
        this . turn = null; // the player who's turn it is (self or opponent)
        this . dealer = null; // the player who's turn it is to pick their own color/side
        this . recent_touch = null;
        this . connecting = NO;
        this . connected = NO;
        this . analysis = NO;
        this . draw_captured_piece = NO;
        this . promotion_move = null;
        this . resume_actions = null;
        this . demonstration_a = /* Donald Byrne (W) vs Robert James Fischer (B) */ "6,7,5,5,,6,0,5,2,,2,6,2,4,,6,1,6,2,,1,7,2,5,,5,0,6,1,,3,6,3,4,,4,0,6,0,,2,7,5,4,,3,1,3,3,,3,7,1,5,,3,3,2,4,,1,5,2,4,,2,1,2,2,,4,6,4,4,,1,0,3,1,,0,7,3,7,,3,1,1,2,,2,4,2,3,,2,0,6,4,,5,4,6,3,,1,2,0,4,,2,3,0,5,,0,4,2,5,,1,6,2,5,,5,2,4,4,,6,3,4,1,,3,0,1,2,,5,7,2,4,,4,4,2,5,,4,1,2,3,,5,0,4,0,,4,7,5,7,,6,4,4,2,,2,3,1,2,,4,2,2,4,,5,7,6,7,,2,5,4,6,,6,7,5,7,,4,6,3,4,,5,7,6,7,,3,4,4,6,,6,7,5,7,,4,6,2,5,,5,7,6,7,,0,1,1,2,,0,5,1,4,,0,0,0,4,,1,4,1,2,,2,5,3,7,,7,6,7,5,,0,4,0,6,,6,7,7,6,,3,7,5,6,,7,7,4,7,,4,0,4,7,,1,2,3,0,,6,1,5,0,,5,5,4,7,,2,4,3,3,,4,7,5,5,,5,6,4,4,,3,0,1,0,,1,1,1,3,,7,5,7,4,,7,1,7,3,,5,5,4,3,,6,0,6,1,,7,6,6,7,,5,0,2,3,,6,7,5,7,,4,4,6,5,,5,7,4,7,,2,3,1,4,,4,7,3,7,,3,3,1,5,,3,7,2,7,,6,5,4,6,,2,7,1,7,,4,6,2,5,,1,7,2,7,,0,6,2,6,";
        this . demonstration = this . demonstration_a;
        
        this . initialize = function ()
        
            {
            this . self = new mm_player ();
            this . opponent = new mm_player ();
            this . turn = null;
            
            ajax . get_board ();
            this . reset ();
            this . settle ();

            window . setTimeout ("game . demonstrate (NO);", 2000);

            return;
            };

        this . finalize_cb = function ()

            {
            var message = ((game . dealer == null) && (game . winner == null)) ? null : "Quit?";

            return (message);
            };

        this . reset = function ()
        
            {
            this . reset_pieces (ANIMATE);
    
            this . moves = new Array ();
            this . tape = new Array ();
            this . settled = NO;
            this . winner = null;
            this . turn = null;
            this . turn = (this . self . color == "white") ? this . self : this . turn;
            this . turn = (this . opponent . color == "white") ? this . opponent : this . turn;
            this . recent_touch = null;
            this . resume_actions = new Array ();
            this . analysis = NO;
            this . draw_captured_piece = NO;

            $ ("opponent") . disabled = null;
            $ ("opponent") . value = "";

            return;
            };

        this . reset_pieces = function (animate)
        
            {
            var titles = new Object ();
            var colors = new Object ();

            var layout = new Array
            
                (
                "RNBQKBNR",
                "PPPPPPPP",
                "        ",
                "        ",
                "        ",
                "        ",
                "pppppppp",
                "rnbqkbnr"
                );

            var diagnostics_layout = new Array

                (
                "  qpK  R",
                "        ",
                "  p   p ",
                "        ",
                "        ",
                "        ",
                "   r q  ",
                "    k   "
                );

            titles ["p"] = titles ["P"] = "pawn";
            titles ["n"] = titles ["N"] = "knight";
            titles ["b"] = titles ["B"] = "bishop";
            titles ["r"] = titles ["R"] = "rook";
            titles ["q"] = titles ["Q"] = "queen";
            titles ["k"] = titles ["K"] = "king";

            colors ["p"] = "white";
            colors ["n"] = "white";
            colors ["b"] = "white";
            colors ["r"] = "white";
            colors ["q"] = "white";
            colors ["k"] = "white";

            colors ["P"] = "black";
            colors ["N"] = "black";
            colors ["B"] = "black";
            colors ["R"] = "black";
            colors ["Q"] = "black";
            colors ["K"] = "black";

            this . pieces = new Array ();

            for (var x = 0; x < 8; x ++)
            for (var y = 0; y < 8; y ++)

                {
                var X = (this . self . color == "black") ? 7 - x : x;
                var Y = (this . self . color == "black") ? 7 - y : y;

                var code = layout [Y] . substring (X, X + 1);

                if (code != " ")
                
                    {
                    var piece = new mm_piece (0, 0, colors [code], titles [code]);

                    piece . move ((x - (8 / 2)) * 50, (y - (8 / 2)) * 50, ! ANIMATE);
                    piece . move (x, y, animate);

                    this . pieces . push (piece);
                    }
                }

            return;
            };

        this . play = function ()
        
            {        
            view . show_play_prompt = NO;

            return;
            };

        this . suspend = function (action)
        
            {
            this . resume_actions . push (action);
            view . show_continue_prompt = YES;
            
            return;
            };
        
        this . resume = function ()
        
            {
            while (this . resume_actions . length > 0) window . setTimeout (this . resume_actions . pop (), 0);

            view . show_continue_prompt = NO;
            ajax . update_time_stamp ();
            
            return;
            };

        this . do_move = function (move)

            {
            var piece = this . get_piece (move . fx, move . fy);

            move . piece = piece;
            move . captured_piece = this . get_piece (move . tx, move . ty);

            piece . move (move . tx, move . ty, game . analysis ? ! ANIMATE : ANIMATE);
            piece . promote (move . promotion);
            
            this . moves . push (move);

            if (piece . title == "pawn")
            if (Math . abs (move . fx - move . tx) == Math . abs (move . fy - move . ty))
            if (move . captured_piece == null)

                {
             // en passant

                move . captured_piece = this . get_piece (move . tx, move . fy);
                }

            if (piece . title == "king")
            if (Math . abs (move . fx - move . tx) == 2)
            
                {
             // castling

                if (move . tx < move . fx) this . do_move (new mm_move (0, move . ty, move . tx + 1, move . ty, null));
                if (move . tx > move . fx) this . do_move (new mm_move (7, move . ty, move . tx - 1, move . ty, null));
                }

            if (this . analysis == NO)
            if (this . connected == YES)
            if (this . is_check (piece . color) == NO)
            
                {
                var other_color = (piece . color == "white") ? "black" : "white";

                this . analysis = YES;

                if (this . is_stalemate (other_color)) ajax . set_winner ("stalemate");
                if (this . is_checkmate (other_color)) ajax . set_winner (piece . color);

                this . analysis = NO;
                }

            this . draw_captured_piece = YES;
            this . settled = NO;

            return;
            };
            
        this . undo_move = function ()

            {
            if (this . moves . length > 0)
            
                {
             // Removing the move at this point causes problems with the get_piece method due to captures,
             // so copy it and then remove it after the piece is referenced.

                var move = this . moves [this . moves . length - 1];
                var piece = this . get_piece (move . tx, move . ty);
                
                this . moves . pop ();
                
                move . piece = null;
                move . captured_piece = null;

                piece . move (move . fx, move . fy, game . analysis ? ! ANIMATE : ANIMATE);
                piece . demote (move . promotion);

                if (this . moves . length > 0)
                if (this . moves [this . moves . length - 1] . piece . title == "king")
                if (Math . abs (this . moves [this . moves . length - 1] . tx - this . moves [this . moves . length - 1] . fx) == 2)
                
                    {
                 // castling

                    this . undo_move ();
                    }
                }

            this . draw_captured_piece = NO;
            this . settled = NO;

            return;
            };

        this . redo_move = function ()
        
            {
            var castle = NO;

            if (this . settled)
            if (this . moves . length > 0)

                {
                if (this . moves . length > 1)
                if (this . moves [this . moves . length - 2] . piece . title == "king")
                if (Math . abs (this . moves [this . moves . length - 2] . tx - this . moves [this . moves . length - 2] . fx) == 2)
                if (Math . abs (this . moves [this . moves . length - 2] . ty - this . moves [this . moves . length - 2] . fy) == 0)

                    {
                    castle = YES;
                    }
                
                if (castle)
                
                    {
                    this . step = YES;
                    this . tape . push (this . moves [this . moves . length - 2]); // king
                    this . undo_move (); // rook then king
                    }

                else

                    {
                    this . step = YES;
                    this . tape . push (this . moves [this . moves . length - 1]);
                    this . undo_move ();
                    }
                }
            
            return;
            };

        this . set_promotion = function (title)
        
            {
            view . show_promotion_prompt = NO;

            game . promotion_move . piece . title = title;
            game . promotion_move . promotion = title;

            ajax . set_move (game . get_move_count () - 1, game . promotion_move);

            if (game . analysis == NO)
            if (game . connected == YES)
            
                {
                var other_color = (game . promotion_move . piece . color == "white") ? "black" : "white";

                game . analysis = YES;

                if (game . is_stalemate (other_color)) ajax . set_winner ("stalemate");
                if (game . is_checkmate (other_color)) ajax . set_winner (game . promotion_move . piece . color);

                game . analysis = NO;
                }

            game . promotion_move = null;

            return;
            };

        this . was_moved = function (piece)
        
            {
            var moved = NO;
            
            if (piece != null)
            if (this . moves != null)
            
                {
                for (var i = 0; i < this . moves . length; i++)
                
                    {
                    if (this . moves [i] . piece == piece) moved = YES;
                    if (moved) break;
                    }
                }
            
            return (moved);
            };

        this . was_captured = function (piece)
        
            {
            var captured = NO;

            for (var i = 0; i < this . moves . length; i++)
            
                {
                if (this . moves [i] . captured_piece == piece) captured = YES;
                if (captured) break;
                }
            
            return (captured);
            };

        this . was_recently_captured = function (piece)
        
            {
            var captured = NO;
            
            if (this . moves . length > 0)
            
                {
                if (this . moves [this . moves . length - 1] . captured_piece == piece) captured = YES;
                }
            
            return (captured);
            };
            
        this . get_piece = function (x, y)
        
            {
            var piece = null;

            for (var i = 0; i < this . pieces . length; i ++)
            
                {
                if (this . was_captured (this . pieces [i]) == NO)
                if (this . pieces [i] . x == x)
                if (this . pieces [i] . y == y)
                
                    {
                    piece = this . pieces [i];
                    
                    break;
                    }
                }

            return (piece);
            };

        this . settle = function ()
        
            {        
            var done = YES; // assumption

            for (var i = 0; i < this . pieces . length; i ++)
            
                {
                var piece = this . pieces [i];
                
                if (this . was_captured (piece) == NO)
                if (piece != this . selected_piece)
                
                    {
                    piece . x_offset = (Math . abs (piece . x_offset) < 0.01) ? 0 : piece . x_offset * SETTLE_FACTOR;
                    piece . y_offset = (Math . abs (piece . y_offset) < 0.01) ? 0 : piece . y_offset * SETTLE_FACTOR;

                    if (piece . x_offset != 0) done = NO;
                    if (piece . y_offset != 0) done = NO;
                    }
                }

            this . settled = done;

            if (this . settled)
            
                {
                this . draw_captured_piece = NO;

                if ((this . step_through == NO) || (this . step == YES))
                if (this . tape != null)
                if (this . tape . length > 0)
                
                    {
                    this . do_move (this . tape . shift ());
                    this . settled = NO;
                    this . step = NO;
                    }
                }

            window . setTimeout ("game . settle ();", this . settled ? LONG_SETTLE_DELAY : SHORT_SETTLE_DELAY);

            return;
            };

        this . t_select_piece_cb = function (event)
            
            {
            if (event . touches . length == 1)

                {
                game . recent_touch = event . touches [0];
                game . select_piece_cb (game . recent_touch);

                event . preventDefault ();
                }
            
            return;
            };

        this . select_piece_cb = function (event)

            {
            if (game . settled)
            if (game . turn == game . self)

                {
                var sc = view . get_square_coordinates (event, INTEGER);

                game . selected_piece = game . get_piece (sc . x, sc . y);

                if (game . selected_piece != null)
                if (game . selected_piece . color != game . turn . color)

                    {
                    game . selected_piece = null;
                    }
                
                if (game . selected_piece != null)
                
                    {
                    game . move_piece_cb (event);
                    }
                }

            return;
            };

        this . t_move_piece_cb = function (event)
            
            {
            if (event . touches . length == 1)
            
                {
                game . recent_touch = event . touches [0];
                game . move_piece_cb (game . recent_touch);

                event . preventDefault ();
                }
            
            return;
            };

        this . move_piece_cb = function (event)

            {
            if (game . selected_piece != null)
            
                {
                var sc = view . get_square_coordinates (event, ! INTEGER);

                game . selected_piece . hold (sc . x - 0.5, sc . y - 0.5);
                }
            
            return;
            };
            
        this . t_deselect_piece_cb = function (event)
            
            {
            if (game . recent_touch != null)

                {
                game . deselect_piece_cb (game . recent_touch);
                game . recent_touch = null;

                event . preventDefault ();
                }

            return;
            };

        this . deselect_piece_cb = function (event)

            {
            if (game . selected_piece != null)

                {
                var isc = view . get_square_coordinates (event, INTEGER);
                var rsc = view . get_square_coordinates (event, ! INTEGER);
                var move = new mm_move (game . selected_piece . x, game . selected_piece . y, isc . x, isc . y, null);

                if (game . is_legal (move))
                if ((game . get_piece (move . tx, move . ty) == null) || (game . get_piece (move . tx, move . ty) . title != "king"))
                
                    {
                    var number = game . get_move_count ();

                    game . do_move (move);
                    game . selected_piece . move (rsc . x - 0.5, rsc . y - 0.5, ! ANIMATE);
                    game . selected_piece . move (isc . x, isc . y, ANIMATE);
                    
                    if (game . is_check (game . selected_piece . color))
                    
                        {
                        var move = game . moves [game . moves . length - 1];

                        game . undo_move ();
                        game . selected_piece . move (rsc . x - 0.5, rsc . y - 0.5, ! ANIMATE);
                        game . selected_piece . move (move . fx, move . fy, ANIMATE);
                        }
                    
                    else
                    
                        {
                        if ((game . selected_piece . title == "pawn") && ((game . selected_piece . y == 0) || (game . selected_piece . y == 7)))

                            {
                            game . promotion_move = move;
                            view . show_promotion_prompt = YES;

                         // game::set_promotion calls ajax::set_move
                            }
                        
                        else
                        
                            {
                            ajax . set_move (number, move);
                            }
                        }
                    }
                
                game . selected_piece = null;
                }

            return;
            };

        this . get_move_count = function ()
        
            {
            var count = 0;
            
            for (var i = 0; i < this . moves . length; i ++)
            
                {
                count ++;

                if (this . moves [i] . piece . title == "king")
                if (Math . abs (this . moves [i] . tx - this . moves [i] . fx) == 2)
                
                    {
                    count --;
                    }
                }
            
            return (count);
            };

        this . is_legal = function (move)
        
            {
            var good_form = NO;
            var clear_to_land = NO;
            var clear_to_stride = NO;

            var piece = this . get_piece (move . fx, move . fy);
            var captured_piece = this . get_piece (move . tx, move . ty);

            var fx = move . fx;
            var fy = move . fy;
            var tx = move . tx;
            var ty = move . ty;
            var dx = Math . abs (tx - fx);
            var dy = Math . abs (ty - fy);

            if ((0 <= move . tx) && (move . tx <= 7))
            if ((0 <= move . ty) && (move . ty <= 7))

                {
                var is_advance = (game . self . color == null) ? ((piece . color == "white") ? (ty < fy) : (ty > fy)) : ((piece . color == game . self . color) ? (ty < fy) : (ty > fy));
                var is_retreat = ! is_advance;

                if (piece . title == "pawn")
                
                    {
                    if (is_advance && (dx == 0) && (dy == 1)) good_form = YES;
                    if (is_advance && (dx == 1) && (dy == 1)) good_form = YES;
                    if (is_advance && (dx == 0) && (dy == 2) && (! this . was_moved (piece))) good_form = YES;
                    
                    if (is_advance && (dx == 0) && (dy == 1) && (captured_piece == null)) clear_to_land = YES;
                    if (is_advance && (dx == 1) && (dy == 1) && (captured_piece == null) && this . is_en_passant (move)) clear_to_land = YES;
                    if (is_advance && (dx == 1) && (dy == 1) && (captured_piece != null)) clear_to_land = YES;
                    if (is_advance && (dx == 0) && (dy == 2) && (captured_piece == null)) clear_to_land = YES;

                    clear_to_land = clear_to_land && ((captured_piece == null) || (piece . color != captured_piece . color));
                    clear_to_stride = this . is_clear (fx, fy, tx, ty);
                    }

                if (piece . title == "knight")
                
                    {
                    if ((dx == 1) && (dy == 2)) good_form = YES;
                    if ((dx == 2) && (dy == 1)) good_form = YES;

                    clear_to_land = (captured_piece == null) || (captured_piece . color != piece . color);
                    clear_to_stride = YES;
                    }

                if (piece . title == "bishop")
                
                    {
                    if (dx == dy) good_form = YES;

                    clear_to_land = (captured_piece == null) || (captured_piece . color != piece . color);
                    clear_to_stride = this . is_clear (fx, fy, tx, ty);
                    }

                if (piece . title == "rook")

                    {
                    if (dx == 0) good_form = YES;
                    if (dy == 0) good_form = YES;

                    clear_to_land = (captured_piece == null) || (captured_piece . color != piece . color);
                    clear_to_stride = this . is_clear (fx, fy, tx, ty);
                    }

                if (piece . title == "queen")
                
                    {
                    if (dx ==  0) good_form = YES;
                    if (dy ==  0) good_form = YES;
                    if (dx == dy) good_form = YES;

                    clear_to_land = (captured_piece == null) || (captured_piece . color != piece . color);
                    clear_to_stride = this . is_clear (fx, fy, tx, ty);
                    }
                
                if (piece . title == "king")
                
                    {
                    if ((dx == 0) && (dy == 1)) good_form = YES;
                    if ((dx == 1) && (dy == 0)) good_form = YES;
                    if ((dx == 1) && (dy == 1)) good_form = YES;
                    if ((dx == 2) && (dy == 0)) good_form = YES;
                    
                    clear_to_land = (captured_piece == null) || (captured_piece . color != piece . color);
                    clear_to_stride = this . is_clear (fx, fy, tx, ty);

                    if (dx == 2)
                    if (dy == 0)
                    
                        {
                     // castling

                        if ((! this . was_moved (piece)) && this . is_safe (fx, fy, tx, ty, piece . color) && (! this . is_check (piece . color)))

                            {
                            var p = null;

                            if ((piece . color != this . self . color) && (move . tx < move . fx)) p = this . get_piece (0, 0);
                            if ((piece . color != this . self . color) && (move . tx > move . fx)) p = this . get_piece (7, 0);
                            if ((piece . color == this . self . color) && (move . tx < move . fx)) p = this . get_piece (0, 7);
                            if ((piece . color == this . self . color) && (move . tx > move . fx)) p = this . get_piece (7, 7);

                            if ((p == null) || (this . was_moved (p)))

                                {
                                good_form = NO;
                                clear_to_land = NO;
                                clear_to_stride = NO;
                                }
                            }
                        
                        else
                        
                            {
                            good_form = NO;
                            clear_to_land = NO;
                            clear_to_stride = NO;
                            }
                        }
                    }
                }

            return (good_form && clear_to_land && clear_to_stride);
            };

        this . is_en_passant = function (move)
        
            {
            var en_passant = NO;

            if (this . moves . length > 0)
            if (this . moves [this . moves . length - 1] . piece == this . get_piece (move . tx, move . fy))
            if (this . moves [this . moves . length - 1] . piece . title == "pawn")
            if (Math . abs (this . moves [this . moves . length - 1] . fy - this . moves [this . moves . length - 1] . ty) == 2)
            
                {
                en_passant = YES;
                }

            return (en_passant);
            };

        this . is_clear = function (fx, fy, tx, ty)
        
            {
            var clear = NO;
            
            var dx = Math . abs (tx - fx);
            var dy = Math . abs (ty - fy);

            if ((dx == 0) || (dy == 0) || (dx == dy)) // vertical, horizontal, or diagonal
            
                {
                var xs = (tx == fx) ? 0 : ((tx < fx) ? -1 : +1);
                var ys = (ty == fy) ? 0 : ((ty < fy) ? -1 : +1);
                
                var x = fx + xs;
                var y = fy + ys;

                var i = 1;
                var c = (dx > dy) ? dx : dy;

                clear = YES; // assumption
                
                while (i < c)
                
                    {
                    if (this . get_piece (x, y) != null)
                    
                        {
                        clear = NO;
                        
                        break;
                        }
                    
                    x = x + xs;
                    y = y + ys;
                    
                    i++;
                    }
                }

            return (clear);
            };

        this . is_safe = function (fx, fy, tx, ty, for_color)
        
            {
         // returns YES if a square on the path (exclusive of the endpoints) is under attack by the other side

            var safe = NO;

            if (((fx - tx) == 0) || ((fy - ty) == 0) || (Math . abs (fx - tx) == Math . abs (fy - ty)))
                
                {
                var dx = (fx == tx) ? 0 : ((fx < tx) ? +1 : -1);
                var dy = (fy == ty) ? 0 : ((fy < ty) ? +1 : -1);

                var sx = fx + dx;
                var sy = fy + dy;

                safe = YES; // assumption

                while ((sx < tx) && safe)
                
                    {
                    for (var i = 0, c = this . pieces . length; i < c; i++)
                    
                        {
                        var piece = this . pieces [i];

                        if (this . was_captured (piece) == NO)
                        if (piece . color != for_color)
                        if (this . is_legal (new mm_move (piece . x, piece . y, sx, sy, null)))
                            
                            {
                            safe = NO;

                            break;
                            }
                        }
                        
                    sx = sx + dx;
                    sy = sy + dy;
                    }
                }

            return (safe);
            };

        this . is_check = function (king_color)
            
            {
            var check = NO;
            var king = null;
            
        
         // find the king

            for (var i = 0; i < this . pieces . length; i ++)
            
                {
                var piece = this . pieces [i];

                if (piece . color == king_color)
                if (piece . title == "king")
                
                    {
                    king = piece;

                    break;
                    }
                }


         // search for a move that can capture the king

            for (var i = 0; i < this . pieces . length; i ++)
            
                {
                var piece = this . pieces [i];

                if (this . was_captured (piece) == NO)
                if (piece . color != king_color)
                if (this . is_legal (new mm_move (piece . x, piece . y, king . x, king . y, null)))
                    
                    {
                    check = YES;

                    break;
                    }
                }

            return (check);
            };
            
        this . is_stalemate = function (king_color)
        
            {
            var stalemate = NO;


         // Only two king remaining.

            if (stalemate == NO)
            
                {
                var count = 0;
                
                for (var i = 0; i < this . pieces . length; i ++) if (this . was_captured (this . pieces [i]) == NO) count ++;
                
                if (count == 2) stalemate = YES;
                }


         // other

            if (stalemate == NO)
            if (! this . is_check (king_color))
            
                {
                stalemate = YES; // assumption

                for (var i = 0; (i < this . pieces . length) && stalemate; i ++)
                
                    {
                    var piece = this . pieces [i];
                    
                    if (this . was_captured (piece) == NO)
                    if (piece . color == king_color)
                    
                        {
                        for (var x = 0; (x < 8) && stalemate; x ++)
                        for (var y = 0; (y < 8) && stalemate; y ++)
                        
                            {
                            var move = new mm_move (piece . x, piece . y, x, y, null);

                            if (this . is_legal (move))
                            
                                {
                                game . analysis = YES;
                                game . do_move (move);

                                if (! game . is_check (king_color))
                                
                                    {
                                    stalemate = NO;
                                    }
                                
                                game . undo_move ();
                                game . analysis = NO;
                                }
                            }
                        }
                    }
                }

            return (stalemate);
            };

        this . is_checkmate = function (king_color)
        
            {
            var checkmate = NO;

            if (this . is_check (king_color))
            
                {
                checkmate = YES; // assumption

                for (var i = 0; (i < this . pieces . length) && checkmate; i ++)
                
                    {
                    var piece = this . pieces [i];
                    
                    if (this . was_captured (piece) == NO)
                    if (piece . color == king_color)
                    
                        {
                        for (var x = 0; (x < 8) && checkmate; x ++)
                        for (var y = 0; (y < 8) && checkmate; y ++)
                        
                            {
                            var test_move = new mm_move (piece . x, piece . y, x, y, null);

                            if (this . is_legal (test_move))

                                {
                                var check_piece = this . get_piece (test_move . tx, test_move . ty);

                                if ((check_piece == null) || (check_piece . title != "king"))

                                    {
                                    this . analysis = YES;
                                    this . do_move (test_move);

                                    if (! this . is_check (king_color)) checkmate = NO;

                                    this . undo_move ();
                                    this . analysis = NO;
                                    }
                                }
                            }
                        }
                    }
                }

            return (checkmate);
            };

        this . freeze_pieces = function ()
        
            {
            for (var p = 0; p < game . pieces . length; p ++)
            
                {
                var piece = game . pieces [p];
                
                piece . freeze ();
                }

            return;
            };

        this . unfreeze_pieces = function ()
        
            {
            for (var p = 0; p < game . pieces . length; p ++)
            
                {
                var piece = game . pieces [p];
                
                piece . unfreeze ();
                }

            return;
            };

        this . start_ai = function ()
        
            {
            this . opponent . artificial = YES;
            this . opponent . number = this . self . number;

            ajax . connect (this . opponent . number);
            
            return;
            };

        this . alternate_turn = function ()

            {
            if (game . turn != null) game . turn = (game . turn == game . self) ? game . opponent : game . self;

            return;        
            };

        this . demonstrate = function (step_through)

            {
            this . step_through = step_through; // YES or NO
            this . step = NO;
            
            this . reset_pieces (! ANIMATE);
            this . tape = new Array ();
            
            if (this . demonstration != null)
            
                {
                var data = this . demonstration . split (",");
                
                for (var i = 0; i < data . length; i = i + 5)
                
                    {
                    var fx = data [i + 0];
                    var fy = data [i + 1];
                    var tx = data [i + 2];
                    var ty = data [i + 3];

                    var promotion = (data [i + 4] == "") ? null : data [i + 4];

                    this . tape . push (new mm_move (fx, fy, tx, ty, promotion));
                    }
                }

            return;
            };

        this . print_diagnostics = function ()

            {
            var p = this . pieces . length + ";";
            
            for (var i = 0; i < this . pieces . length; i ++) p = p + " {" + this . pieces [i] . x + ", " + this . pieces [i] . y + "}";
            
            console . log (p);
            
            return;
            };
        
        this . replay_game = function (player_a, player_b)
        
            {
            if (player_a != "")
            if (player_b != "")
            
                {
                ajax . get_moves (player_a, player_b);
                }
            
            return;
            };
        };