Beispiel #1
0
 private static bool CheckDirection(GridModel grid, Direction direction)
 {
     var vector = Vector.FromDirection(direction);
     foreach (int x in grid.TraversalsX(direction))
     {
         foreach (int y in grid.TraversalsY(direction))
         {
             var cell1 = grid[x, y];
             var cell2 = grid[x + vector.x, y + vector.y];
             if (cell1 != null && cell2 != null)
             {
                 if (cell1.IsOccupied && cell2.IsAvailable)
                 {
                     return true;
                 }
                 if (cell1.IsOccupied && cell2.IsOccupied)
                 {
                     if (cell1.Tile.Value == cell2.Tile.Value)
                         return true;
                 }
             }
         }
     }
     return false;
 }
Beispiel #2
0
        private double Maximizer(GridModel grid, int depth, double alpha, double beta)
        {
            double bestScore = alpha;
            foreach (var direction in Vector.Directions) 
            {
                var newGrid = new GridModel(grid);
                int deltaScore;
                if (PlayerTurn.Move(newGrid, direction, out deltaScore)) 
                {
                    if (depth == 0) 
                    {
                        // leaf node, static evaluation
                        return StaticEvaluation(newGrid);
                    }
                    else
                    {
                        alpha = Math.Max(alpha, AlphaBetaSearch(newGrid,  depth-1, false, alpha, beta));
                    }

                    if (alpha > beta) // pruning
                    {
                        return beta;
                    }
                }
            }
            return alpha;
        }
Beispiel #3
0
        public override Direction ComputeNextMove(GridModel grid)
        {
            var input = GridToInput(grid);
            var output = _network.Compute(input);

            // if output is impossible teach->0
            // if output has many merges teach good
            // learning mode from human

            Direction result = OutputToDirection(output);
            var validMoves = ValidDirections(grid);
            if (validMoves.Contains(result))
                return result;

            foreach (Direction direction in validMoves)
            {
                TrainNextMove(grid, direction);
            }
            /*
            int validMoveCount = validMoves.Count();
            if (validMoveCount > 0)
                TrainNextMove(grid, validMoves.Last());
             */
            return Direction.None;
        }
Beispiel #4
0
 protected Direction[] ValidDirections(GridModel grid)
 {
     Dictionary<Direction, bool> d = new Dictionary<Direction, bool>();
     foreach (Direction direction in Vector.Directions)
     {
         d[direction] = CheckDirection(grid, direction);
     }
     return d.Where(x => x.Value).Select(x => x.Key).ToArray();
 }
Beispiel #5
0
 /// <summary>
 ///     Adds a tile in a random position
 /// </summary>
 public void Move(GridModel grid)
 {
     var cells = grid.AvailableCells();
     if (cells.Any())
     {
         int value = _rand.NextDouble() < _percentageOfValue1 ? 1 : 2;
         var tile = new TileModel(value);
         var position = cells[_rand.Next(cells.Length)];
         tile.Position = position;
     }
 }
Beispiel #6
0
        public GameModel()
        {
            _playerTurn = new PlayerTurn();
            _computerTurn = new ComputerTurn();

            Score = 0;
            HighScore = 0;

            StartTiles = 2;
            KeepPlaying = true;

            Grid = new GridModel(4);
        }
Beispiel #7
0
        public bool Move(GridModel grid, Direction direction, out int score)
        {
            score = 0;
            if (direction == Direction.None)
                return false;

            var vector = Vector.FromDirection(direction);
            var moved = false;
            var alreadyMerged = new List<TileModel>();

            // Traverse the grid in the right direction and move tiles
            foreach (int x in grid.TraversalsX(direction))
            {
                foreach (int y in grid.TraversalsY(direction))
                {
                    CellModel cell = grid[x, y];
                    if (cell.IsOccupied)
                    {
                        TileModel tile = cell.Tile;
                        CellModel nextCell;
                        CellModel farthestCell = grid.FindFarthestPosition(cell, vector, out nextCell);

                        TileModel nextTile = (nextCell == null) ? null : nextCell.Tile;

                        // Only one merger per row traversal?
                        if (nextTile != null && nextTile.Value == tile.Value && !alreadyMerged.Contains(nextTile))
                        {
                            var merged = new TileModel(tile, nextTile);
                            alreadyMerged.Add(merged);
                            tile.Position = null;
                            nextTile.Position = null;
                            merged.Position = nextCell;
                            moved = true;

                            // Update the score
                            score += (int)Math.Pow(2, merged.Value);
                        }
                        else
                        {
                            var oldPosition = tile.Position;
                            tile.Position = farthestCell;
                            if ((oldPosition.PosX != tile.Position.PosX)
                                || (oldPosition.PosY != tile.Position.PosY))
                                moved = true;
                        }
                    }
                }
            }
            return moved;
        }
Beispiel #8
0
 private static double[] GridToInput(GridModel grid)
 {
     var input = new double[16];
     for (int i = 0; i < 16; i++)
         input[i] = grid[i].Value;
     double max = input.Max();
     if (max > 0)
     {
         // scale all inputs
         for (int i = 0; i < 16; i++)
             input[i] = input[i] / max;
     }
     return input;
 }
Beispiel #9
0
 // use a neural network to compute a score out of different simulated moves
 private double StaticEvaluation(GridModel grid, Direction direction)
 {
     var newGrid = new GridModel(grid);
     int score;
     if (PlayerTurn.Move(newGrid, direction, out score))
     {
         var input = new double[16];
         for (int i = 0; i < 16; i++)
             input[i] = newGrid[i].Value;
         var output = _network.Compute(input);
         return output[0];
     }
     return double.NegativeInfinity;
 }
Beispiel #10
0
 public override Direction ComputeNextMove(GridModel grid)
 {
     Direction bestMove = Direction.None;
     double bestScore = double.NegativeInfinity;
     foreach (var direction in Vector.Directions)
     {
         var score = StaticEvaluation(grid, direction);
         if (score > bestScore)
         {
             bestScore = score;
             bestMove = direction;
         }
     }
     return bestMove;
 }
Beispiel #11
0
 public override Direction ComputeNextMove(GridModel grid)
 {
     int maxMerges = 0;
     var dirs = ValidDirections(grid);
     Direction move = dirs[_rand.Next(dirs.Length)];
     foreach (Direction direction in Vector.Directions)
     {
         int merges = CountMerges(direction, grid);
         if (merges > maxMerges)
         {
             maxMerges = merges;
             move = direction;
         }
     }
     return move;
 }
Beispiel #12
0
 /// <summary>
 ///     Create a deep copy of a grid, history of moves and merges removed.
 /// </summary>
 /// <param name="grid"></param>
 public GridModel(GridModel grid)
 {
     SizeX = grid.SizeX;
     SizeY = grid.SizeY;
     _cells = new List<CellModel>(SizeX * SizeY);
     for (int i = 0; i < SizeX * SizeY; i++)
     {
         int x = i % SizeY;
         int y = i / SizeX;
         var cell = new CellModel(x, y);
         _cells.Add(cell);
         var sourceCell = grid[x,y];
         if (sourceCell.IsOccupied)
         {
             var tileCopy = new TileModel(sourceCell.Tile);
             tileCopy.Position = cell;
         }
     }
 }
Beispiel #13
0
        private static double StaticEvaluation(GridModel grid)
        {
            double
                smoothWeight = 0.1,
                monoWeight   = 0.1,
                //islandWeight = 0.0,
                mono2Weight = 1.0,
                emptyWeight = 2.7,
                maxWeight = 1.0;

            return
                //  grid.Smoothness() * smoothWeight
                //grid.Monotonicity() * monoWeight
                //- grid.islands() * islandWeight
                + grid.Monotonicity2() * mono2Weight
                //+ Math.Log(grid.EmptyCells()) * emptyWeight
                //+ grid.MaxValue() * maxWeight
                ;
        }
Beispiel #14
0
 public override Direction ComputeNextMove(GridModel grid)
 {
     Direction bestMove = Direction.None;
     double bestScore = double.NegativeInfinity;
     var timer = new Stopwatch();
     timer.Start();
     int depth = 2;
     do
     {
         foreach (var direction in Vector.Directions)
         {
             var score = AlphaBetaSearch(grid, depth, true, Double.NegativeInfinity, Double.PositiveInfinity);
             if (score > bestScore)
             {
                 bestScore = score;
                 bestMove = direction;
             }
         }
         depth++;
     } while (depth < 3);//timer.ElapsedMilliseconds < _minSearchTime);
     return bestMove;
 }
Beispiel #15
0
 private int CountMerges(Direction direction, GridModel grid)
 {
     var vector = Vector.FromDirection(direction);
     int merges = 0;
     foreach (int x in grid.TraversalsX(direction))
     {
         foreach (int y in grid.TraversalsY(direction))
         {
             var cell1 = grid[x, y];
             var cell2 = grid[x + vector.x, y + vector.y];
             if (cell1 != null && cell2 != null)
             {
                 if (cell1.IsOccupied && cell2.IsOccupied)
                 {
                     if (cell1.Tile.Value == cell2.Tile.Value)
                         merges++;
                 }
             }
         }
     }
     return merges;
 }
Beispiel #16
0
 public void SetGridSize(int value)
 {
     Grid = new GridModel(value);
     Restart();
 }
Beispiel #17
0
        private double Minimizer(GridModel grid, int depth, double alpha, double beta)
        {
            // computer's turn, we'll do heavy pruning to keep the branching factor low
            var availableCells = grid.AvailableCells().ToList();

            // try out all combinations possible
            foreach (var value in new int[] { 1, 2 })
            {
                foreach (CellModel cell in availableCells)
                {
                    var tile = new TileModel(value);
                    tile.Position = cell;
                    beta = Math.Min(beta, AlphaBetaSearch(grid, depth, true, alpha, beta));
                    tile.Position = null;

                    if (beta < alpha) // pruning
                    {
                        return alpha;
                    }
                }
            }

            /*
            // try a 2 and 4 in each cell and measure how annoying it is
            // with metrics from eval
            //var scores = { 2: [], 4: [] };
            foreach (var value in new int[] { 1, 2 })
            {
                foreach (CellModel cell in availableCells)
                {
                    scores[value].push(null);

                    var tile = new TileModel(value);
                    tile.Position = cell;

                    scores[value][i] = -grid.Smoothness() + grid.Islands();

                    tile.Position = null;
                }
            }

            // now just pick out the most annoying moves
            var candidates = new List<CellModel>();
            var maxScore = Math.Max(Math.Max(null, scores[2]), Math.Max(null, scores[4]));
            foreach (var value in new int[] { 1, 2 })
            {
                for (var i = 0; i < scores[value].length; i++)
                {
                    if (scores[value][i] >= maxScore)
                    {
                        candidates.Add(cells[i]);
                    }
                }
            }

            // search on each candidate
            foreach (CellModel position in candidates)
            {
      
            }
             */
            return beta;
        }
Beispiel #18
0
 public abstract Direction ComputeNextMove(GridModel grid);
Beispiel #19
0
 private double AlphaBetaSearch(GridModel grid, int depth, bool playerTurn, double alpha, double beta) 
 {
     if (playerTurn)
     {
         return Maximizer(grid, depth, alpha, beta);
     }
     else
     {
         return Minimizer(grid, depth, alpha, beta);
     }
 }
Beispiel #20
0
 public override Direction ComputeNextMove(GridModel grid)
 {
     return Direction.Up;
 }
Beispiel #21
0
        /*
         * Grid.prototype.monotonicity = function() {
         * var self = this;
         * var marked = [];
         * var queued = [];
         * var highestValue = 0;
         * var highestCell = {x:0, y:0};
         * for (var x=0; x<4; x++) {
         * marked.push([]);
         * queued.push([]);
         * for (var y=0; y<4; y++) {
         * marked[x].push(false);
         * queued[x].push(false);
         * if (this.cells[x][y] &&
         * this.cells[x][y].value > highestValue) {
         * highestValue = this.cells[x][y].value;
         * highestCell.x = x;
         * highestCell.y = y;
         * }
         * }
         * }
         *
         * increases = 0;
         * cellQueue = [highestCell];
         * queued[highestCell.x][highestCell.y] = true;
         * markList = [highestCell];
         * markAfter = 1; // only mark after all queued moves are done, as if searching in parallel
         *
         * var markAndScore = function(cell) {
         * markList.push(cell);
         * var value;
         * if (self.cellOccupied(cell)) {
         * value = Math.log(self.cellContent(cell).value) / Math.log(2);
         * } else {
         * value = 0;
         * }
         * for (direction in [0,1,2,3]) {
         * var vector = self.getVector(direction);
         * var target = { x: cell.x + vector.x, y: cell.y+vector.y }
         * if (self.withinBounds(target) && !marked[target.x][target.y]) {
         * if ( self.cellOccupied(target) ) {
         * targetValue = Math.log(self.cellContent(target).value ) / Math.log(2);
         * if ( targetValue > value ) {
         *  //console.log(cell, value, target, targetValue);
         *  increases += targetValue - value;
         * }
         * }
         * if (!queued[target.x][target.y]) {
         * cellQueue.push(target);
         * queued[target.x][target.y] = true;
         * }
         * }
         * }
         * if (markAfter == 0) {
         * while (markList.length > 0) {
         * var cel = markList.pop();
         * marked[cel.x][cel.y] = true;
         * }
         * markAfter = cellQueue.length;
         * }
         * }
         *
         * while (cellQueue.length > 0) {
         * markAfter--;
         * markAndScore(cellQueue.shift())
         * }
         *
         * return -increases;
         * }
         */
        // measures how monotonic the grid is. This means the values of the tiles are strictly increasing
        // or decreasing in both the left/right and up/down directions
        public static double Monotonicity2(this GridModel grid)
        {
            // scores for all four directions
            var totals = new Dictionary <Direction, double>();

            totals[Direction.Up]    = 0;
            totals[Direction.Down]  = 0;
            totals[Direction.Left]  = 0;
            totals[Direction.Right] = 0;

            // up/down direction
            for (var x = 0; x < grid.SizeX; x++)
            {
                var next         = 1;
                var currentValue = grid[x, 0].Value;
                while (next < grid.SizeY)
                {
                    var nextValue = grid[x, next].Value;
                    if (nextValue > 0)
                    {
                        if (currentValue > nextValue)
                        {
                            totals[Direction.Up] += nextValue - currentValue;
                        }
                        else if (nextValue > currentValue)
                        {
                            totals[Direction.Down] += currentValue - nextValue;
                        }
                        currentValue = nextValue;
                    }
                    next++;
                }
            }

            // left/right direction
            for (var y = 0; y < grid.SizeY; y++)
            {
                var next         = 1;
                var currentValue = grid[0, y].Value;
                while (next < grid.SizeX)
                {
                    var nextValue = grid[next, y].Value;
                    if (nextValue > 0)
                    {
                        if (currentValue > nextValue)
                        {
                            totals[Direction.Left] += nextValue - currentValue;
                        }
                        else if (nextValue > currentValue)
                        {
                            totals[Direction.Right] += currentValue - nextValue;
                        }
                        currentValue = nextValue;
                    }
                    next++;
                }
            }

            return(Math.Max(totals[Direction.Left], totals[Direction.Right]) +
                   Math.Max(totals[Direction.Up], totals[Direction.Down]));
        }
Beispiel #22
0
 public static CellModel FindFarthestPosition(this GridModel grid, CellModel cell, Direction direction, out CellModel next)
 {
     return(FindFarthestPosition(grid, cell, Vector.FromDirection(direction), out next));
 }
Beispiel #23
0
 public virtual void TrainNextMove(GridModel grid, Direction direction)
 {
 }
Beispiel #24
0
        public override void TrainNextMove(GridModel grid, Direction direction)
        {
            var input = GridToInput(grid);
            var output = DirectionToOutput(direction);
            double error = _teacher.Run(input, output);

            // also train rotated boards at the same time!
            input = RotateLeft(input);
            output = DirectionToOutput(RotateLeft(direction));
            error = Math.Max(_teacher.Run(input, output), error);

            input = RotateLeft(input);
            output = DirectionToOutput(RotateLeft(direction));
            error = Math.Max(_teacher.Run(input, output), error);

            input = RotateLeft(input);
            output = DirectionToOutput(RotateLeft(direction));
            error = Math.Max(_teacher.Run(input, output), error);
            Console.WriteLine("Error: {0}", error);
        }
Beispiel #25
0
 public void SetGridSize(int value)
 {
     Grid = new GridModel(value);
     Restart();
 }
Beispiel #26
0
 public GridViewModel(GridModel model)
 {
     Model = model;
 }
Beispiel #27
0
 public static double EmptyCells(this GridModel grid)
 {
     return(grid.Cells
            .Count(x => x.IsAvailable));
 }