protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); var parameters = (TetrisInput)e.Parameter; generateGrid(TetrisSettings.Width, TetrisSettings.Height); this.boardOccupation = new bool[width, height]; this.boardFills = new Rectangle[width, height]; this.game = new TetrisGame(width, height); this.ann = new ANN(ANNSettings.Weights, ANNSettings.Input, ANNSettings.Hidden); this.package = new PlacementPackage() { RemovedRows = 0 }; SolidColorBrush blackBrush = new SolidColorBrush(Colors.Black); for (int x = 0; x < this.width; x++) { for (int y = 0; y < this.height; y++) { var block = new Rectangle(); block.Fill = transparentBrush; block.Stroke = blackBrush; Grid.SetColumn(block, x); Grid.SetRow(block, y); board.Children.Add(block); boardFills[x, y] = block; } } }
public void ApplyPackage(PlacementPackage package) { this.board = package.ResultantBoard; this.peaks = package.Peaks; this.Score += package.GetScore(); this.PlacedPieces++; }
public Tuple <double, double> Evaluate(bool full = false) { int evalGames = full ? TetrisSettings.EvalutaionGames : TetrisSettings.TrainEvaluationGames; double[] _clearedRows = new double[evalGames]; double[] _placedPieces = new double[evalGames]; for (int i = 0; i < evalGames; i++) { if (full) { this.tetrisGame.ResetGame(TetrisSettings.Width, TetrisSettings.Height); } else { this.tetrisGame.ResetGame(TetrisSettings.TrainWidth, TetrisSettings.TrainHeight); } PlacementPackage[] packages = tetrisGame.GetPackages(); while (packages.Length > 0) { PlacementPackage bestPackage = packages[0]; bestPackage.PackageScore = ann.Evaluate(bestPackage); for (int iPackage = 1; iPackage < packages.Length; iPackage++) { PlacementPackage package = packages[iPackage]; package.PackageScore = ann.Evaluate(package); if (package.PackageScore > bestPackage.PackageScore) { bestPackage = package; } } tetrisGame.ApplyPackage(bestPackage); packages = tetrisGame.GetPackages(); } _clearedRows[i] = tetrisGame.Score; _placedPieces[i] = tetrisGame.PlacedPieces; } double totCleared = 0, totPlaced = 0; for (int i = 0; i < evalGames; i++) { totCleared += _clearedRows[i]; totPlaced += _placedPieces[i]; } this.cleared = totCleared / evalGames; this.placed = placed / evalGames; return(new Tuple <double, double>(cleared, placed)); }
public double Evaluate(PlacementPackage placement) { placement.Normalize(); double[] intermediateScores = new double[Hidden + 1]; //for (int iIntermediateScore = 0; iIntermediateScore < intermediateScores.Length - 1; iIntermediateScore++) { // intermediateScores[iIntermediateScore] = 0; //} intermediateScores[intermediateScores.Length - 1] = 1; for (int iInput = 0; iInput <= Input; iInput++) // the last gotten feature is the input bias { double feature = placement.GetFeature(iInput); int index = iInput * Hidden; for (int iHidden = 0; iHidden < Hidden; iHidden++) { intermediateScores[iHidden] += feature * Weights[index + iHidden]; } } for (int inter = 0; inter < intermediateScores.Length - 1; inter++) { if (intermediateScores[inter] < 0) { intermediateScores[inter] = 0; } } double result = 0; int outputIndex = (this.Input + 1) * this.Hidden; for (int inter = 0; inter < intermediateScores.Length; inter++) { double temp = intermediateScores[inter] * Weights[outputIndex + inter]; result += temp; } result = 1 / (1 + Math.Pow(Math.E, -result)); return(result); //double result = 0; //for (int i = 0; i < ANNSettings.Input; i++) { // result += placement.GetFeature(i) * Weights[i]; //} //return result; }
private void NNPlace(object sender, RoutedEventArgs e) { PlacementPackage[] packages = game.GetPackages(currentPiece); if (packages.Length == 0) { ClearBoard(); } else { PlacementPackage bestPackage = packages[0]; bestPackage.PackageScore = ann.Evaluate(bestPackage); for (int iPackage = 1; iPackage < packages.Length; iPackage++) { PlacementPackage package = packages[iPackage]; package.PackageScore = ann.Evaluate(package); if (package.PackageScore > bestPackage.PackageScore) { bestPackage = package; } } int[,] toOccupy = bestPackage.OccupiedCells; this.package = bestPackage; List <int> occupiedRows = new List <int>(); for (int i = 0; i < bestPackage.OccupiedCells.GetLength(0); i++) { if (!occupiedRows.Contains(toOccupy[i, 1])) { occupiedRows.Add(bestPackage.OccupiedCells[i, 1]); } } game.ApplyPackage(bestPackage); for (int i = 0; i < toOccupy.GetLength(0); i++) { if (this.boardOccupation[toOccupy[i, 0], toOccupy[i, 1]]) { //Console.WriteLine("thing"); throw new Exception("Something about a thing"); } this.boardOccupation[toOccupy[i, 0], toOccupy[i, 1]] = true; boardFills[toOccupy[i, 0], toOccupy[i, 1]].Fill = currentPiece.Brush; } //Clear the board as necessary, not optimized. List <int> rowsToOccupy = new List <int>(); for (int i = 0; i < toOccupy.GetLength(0); i++) { if (!rowsToOccupy.Contains(toOccupy[i, 1])) { rowsToOccupy.Add(toOccupy[i, 1]); } } /* * The logic is pretty simple, we don't care about efficiency, so start at the bottom row, if it's full, remove it and bring down all the rows above. * Repeat for that row, once done, test the row up, until every row is tested. */ for (int i = this.height - 1; i >= 0; i--) { bool rowFull = true; for (int j = 0; j < this.width; j++) { if (!this.boardOccupation[j, i]) { rowFull = false; break; } } if (rowFull) { for (int j = i - 1; j >= 0; j--) // Start at the row above to send it down { for (int x = 0; x < width; x++) // Move horizontally along the board { boardFills[x, j + 1].Fill = boardFills[x, j].Fill; //Grid.SetColumn(boardFills[x, j + 1], j + 1); this.boardOccupation[x, j + 1] = this.boardOccupation[x, j]; // set the board occupation for later verification. } } i++; for (int x = 0; x < width; x++) { boardFills[x, 0].Fill = transparentBrush; this.boardOccupation[x, 0] = false; } } } // Verify the two different calculations match if (!this.boardOccupation.ToString().Equals(this.game.Board.ToString())) { Console.WriteLine(this.boardOccupation.ToString()); Console.WriteLine(this.game.Board.ToString()); throw new Exception("TetrisGame and Tetris boards do not match"); } display_score.Text = this.game.Score.ToString(); } newPiece(); }
public PlacementPackage[] GetPackages(Piece piece = null) { /* Features: * Removed Rows - Number of rows cleared as result of placement * Smoothness - Sum of difference in height between adjacent columns * Landing Height - The height where the piece is being placed * Number of Holes - Number of surrounded cells (other pieces or edge) * Number of Wells - Cell is closed at bottom, left, and right; but open on the top * Altitude Difference - Highest peak minus lowest peak * Pile Height - Row of highest occupied cell * Maximum Well Depth - The depth of the deepest well * Sum of Well Depths - The sum of all well depths * Covered - The number of empty cells below an occupied cell * Weighted Block Count - The sum of all occupied cells, weighted by the row in which it occurs * Connected Hole Count - Several vertically connected gaps make one connected hole * Block Count - The total number of blocks in the grid. * Eroded Piece Count - cells from current placement removed, multiplied by removed rows. * Row Transitions - The number of transitions between occupied and unoccupied cells in a row, edges are occupied. * Column Transitions - Same as row, but for column. */ List <PlacementPackage> packages = new List <PlacementPackage>(); piece = piece ?? new Piece(); for (int testX = 0; testX < Width; testX++) { List <int[, ]> placements = new List <int[, ]>(); for (int y = this.peaks[testX]; y < Height; y++) { if (y >= 0) { if (!board[y][testX]) { placements.AddRange(piece.GetPlacements(testX, y)); } } } foreach (int[,] placement in placements) { bool valid = false; List <int> rowsWithPlacements = new List <int>(); List <int> cells = new List <int>(); for (int iCell = 0; iCell < placement.GetLength(0); iCell++) { int y = placement[iCell, 1], x = placement[iCell, 0]; if (x < 0 || x >= Width || y < 0 || y >= Height) { valid = false; break; } if (board[y][x]) { valid = false; break; } if (y == Height - 1) { valid = true; } else if (board[y + 1][x]) { valid = true; } if (!rowsWithPlacements.Contains(y)) { rowsWithPlacements.Add(y); cells.Add(1); } else { cells[rowsWithPlacements.IndexOf(y)]++; } } if (valid) { bool[][] result = CopyArray(this.board); for (int iCell = 0; iCell < placement.GetLength(0); iCell++) { result[placement[iCell, 1]][placement[iCell, 0]] = true; } Tuple <int, int> clearResult = ClearRows(rowsWithPlacements.ToArray(), cells.ToArray(), result); PlacementPackage package = new PlacementPackage(result) { // Removed Rows RemovedRows = clearResult.Item1, // Eroded Piece Count ErodedPieceCount = clearResult.Item1 * clearResult.Item2, OccupiedCells = placement }; int[] peaks = Enumerable.Repeat(Height - 1, Width).ToArray(); int lowPeak = 0; int highPeak = TetrisSettings.Height; bool rPrev = true, cPrev = true; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { if (this.board[y][x] != rPrev) { rPrev = !rPrev; // Row Transitions package.RowTransitions++; } } if (this.board[y][Width - 1] == false) { // Row Transitions package.RowTransitions++; } } for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { if (this.board[y][x] != cPrev) { // Column Transitions package.ColumnTransitions++; cPrev = !cPrev; } if (peaks[x] == Height - 1 && result[y][x]) { peaks[x] = y - 1; highPeak = peaks[x] < highPeak ? peaks[x] : highPeak; lowPeak = peaks[x] > lowPeak ? peaks[x] : lowPeak; int depth = 0; for (int i = y - 1; i >= 0; i--) { bool[] res = result[i]; int l = x - 1, r = x + 1; if ((l < 0 || res[l]) && (r == Width || res[r])) { depth++; } else { break; } } if (depth > 0) { // Number of Wells package.NumberOfWells++; // Sum of all Well depths package.TotalWellDepth += depth; // Max Well Depth if (depth > package.MaxWellDepth) { package.MaxWellDepth = depth; } } } if (peaks[x] != Height - 1) { if (!result[y][x]) { int r = x + 1, l = x - 1, u = y - 1, d = y + 1; bool[] res = result[y]; // Covered package.Covered++; if ((r == Width || res[r]) && (l < 0 || res[l]) && (u < 0 || result[u][x])) { if (d == Height || result[d][x]) { // Number of Holes package.NumberOfHoles++; } else { // Connected Hole Count package.ConnectedHoleCount++; } } } else { // Weighted Block Count package.WeightedBlockCount += y; // Block Count package.BlockCount++; } } else { lowPeak = Height - 1; } } if (this.board[Height - 1][x] == false) { package.ColumnTransitions++; } } for (int x = 0; x < Width - 1; x++) { // Smoothness package.Smoothness += Math.Abs(peaks[x] - peaks[x + 1]); } // Landing Height package.LandingHeight = Height - LowestPoint(placement); //Altitude Difference package.AltitudeDifference = Math.Abs(highPeak - lowPeak); // Pile Height package.PileHeight = TetrisSettings.Height - highPeak; package.Peaks = peaks; packages.Add(package); } } } return(packages.ToArray()); }