static void Main() { string firstLine = Console.ReadLine(); // reads the first line from console StreamReader file = null; // since there is option to read data from file, opens empty StreamReader bool hasFileData = false; // by default the data should be read from console if (firstLine[0] == '@') // but if the first character is "@" (non digit), that means the content is filename (and path) { hasFileData = true; // then all input data should be read from the file firstLine = firstLine.Replace('@', ' ').Trim(); // removes "@" and any leading/trailing spaces file = new StreamReader(firstLine); // opens the file for reading } int C; int N; if (hasFileData) // reads from file { C = int.Parse(file.ReadLine()); N = int.Parse(file.ReadLine()); } else //reads from Console { C = int.Parse(firstLine); N = int.Parse(Console.ReadLine()); } if (N == 1) // marginal case, one dimensional playfield { int height = int.Parse(Console.ReadLine()); // reads the single tower for (int i = 0; i < C; i++) { Console.WriteLine("take 0 0"); // for a single tower playfiled only C times "take" move is allowed } return; // then program finishes } Playfield theBoard = new Playfield(N); // creates empty playfield for (int i = 0; i < N; i++) // iterates through rows { string[] Row; if (hasFileData) Row = file.ReadLine().Split(' '); // reads each line from file and separates it to array of string values else Row = Console.ReadLine().Split(' '); // reads each line from console and separates it to array of string values for (int j = 0; j < Row.Length; j++) // iterates through cells in current row { theBoard.Set(i, j, int.Parse(Row[j])); // and pumps up the Playfield } } if (hasFileData) // if the data source is file { file.Close(); // closes that file file.Dispose(); // releases all used resources } int thsIterations = C * N * N / 1000; //calculates some estimation of the number of interations required // this should be used to select proper algorythm in order to fit into the 3 sec. time limit // Choosing algorythm depending on board and moves complexity in order to fit within 3 seconds calculation interval if (thsIterations < 6000) // for small and medium range a complex algorithm is selected { Move bestMove = new Move(); //FULL WALKTROUGH - on each level (move's depth) the best result is selected for (int depth = 0; depth < C; depth++) // iterates through possible moves { int maxRank = int.MinValue; // the resultng rank, initially the minimal possible value for (int r = 0; r < N; r++) // iterates through possible rows for (int c = 0; c < N; c++) // iterates through possible colunms for (int m = 0; m < 2; m++) // iterates through possible type of moves { int diff = theBoard.NeighboursDiff(bestMove.row, bestMove.column) - theBoard.NeighboursDiff(r, c); // calculates if the neighbour difference of best move and current int rank = theBoard.Score; // gets current score of the board if (m == 0 && theBoard.Get(r, c) == 0) m = 1; // checks for invalid TAKE move, goes directly to PUT if (m == 0) rank = rank - theBoard.Take(r, c); // take = 0 and calculates the rank of the move else rank = rank - theBoard.Put(r, c); // put = 1 and calculates the rank of the move // SELECTION OF THE BEST MOVE if (rank > maxRank || // 1. (rank == maxRank && diff > 0) || // 2. and 3. (rank == maxRank && diff == 0 && theBoard.Get(r, c) >= theBoard.Get(bestMove.row, bestMove.column))) // 2. and 4. // a better move has been found if one of the following three conditons has been reached: // 1. the current rank is better than the previous one (or) // 2. the current rank is equal to previous one, but // 2.1. the difference with some neighbours is less, (or) // 3. the current rank and the differnece are equal to previous one, but // 3.1. but current height is greater { maxRank = rank; // assigns better rank of the move bestMove.type = m; // take = 0, put = 1 bestMove.row = r; // row bestMove.column = c; // column } theBoard.Resume(); // returns to previous move } // now makes the best move over all iterations of current depth if (bestMove.type == 0) { theBoard.Take(bestMove.row, bestMove.column); // take = 0 Console.Write("take "); } else { theBoard.Put(bestMove.row, bestMove.column); // put = 1 Console.Write("put "); } Console.WriteLine("{0} {1}", bestMove.row, bestMove.column); } } else // for most complex boards only one iteration is possible (or half! or less!!) { // creates a pair of arrays for moves and their ranks Move[] moves = new Move[C]; int[] ranks = new int[C]; int rowFactor = 1; // normally all cells in the board are reached if (thsIterations > 300000) rowFactor = 2 + (thsIterations > 650000 ? 1 : 0); // unfortunately in order to fit into the time limit, for most complex boards only a half/third of them could be reached //SINGLE WALKTROUGH - on each iteration the result is put into the sorted array of ranks for (int r = 0; r < N; r = r + rowFactor) // iterates through possible rows (or half of them) for (int c = 0; c < N; c++) // iterates through possible colunms for (int m = 0; m < 2; m++) // iterates through possible type of moves { int rank = theBoard.Score; // gets current score of the board if (m == 0 && theBoard.Get(r, c) > 0) rank = rank - theBoard.Take(r, c); // take = 0 and calculates the rank of the move else { rank = rank - theBoard.Put(r, c); m = 1; // put = 1 and calculates the rank of the move } int i = 0; while (i < C && rank <= ranks[i]) i++; // looks where the current rank is suitable to place if (i < C) // only if the place has found { if (moves[i].Equals(null)) // if the list is not full makes new item moves[i] = new Move(); else //and finally we found the place to insert a new better move for (int k = C - 1; k > i; k--) { moves[k] = moves[k - 1]; ranks[k] = ranks[k - 1]; } moves[i].type = m; moves[i].row = r; moves[i].column = c; ranks[i] = rank; } theBoard.Resume(); // returns to previous move } // prints first C best ranked moves for (int i = 0; i < C; i++) // iterates through alreeady sorted best moves { if (moves[i].type == 0) { theBoard.Take(moves[i].row, moves[i].column); // take = 0 Console.Write("take "); } else { theBoard.Put(moves[i].row, moves[i].column); // put = 1 Console.Write("put "); } Console.WriteLine("{0} {1}", moves[i].row, moves[i].column); } } }
static void Main() { Console.WriteLine(@" **************************** * * * Trolls Game Visualizer * * * * author: Pavel Sotirov * **************************** Here you must enter input data as per the rules and conditions set in the ""Troll Game"" competition, organized by Telerik and PCMagazine. More details you can obtain here: http://konkurs.pcmagbg.net/task-1-season-2012-2013/ The game visialization will proceed in the following steps: 1. Entering the number of moves, board dimensions and data. You can select two ways to enter the board - by using already prepared text file or manually via the console. If you omit a filename when asked, that means you are choosing manual way. 2. Solving the board. Please be patient! Depending on board's dimensions and number of desired moves, this process can take some time - up to four minutes. You will be informed about the progress. 3. Creating output HTML files For each move a corresponding HTML file will be created that will hold the board's content after the move. For each move a separate file will be created with move's index. Start file with initial board content (0 index) also will be created. You will be asked to enter the proper filename. If this filename is omited or invalid, a default filename of ""Trolls xxxx.html"" will be used (xxxx is move's index from 0 to 1000). 4. Finally the default browser will be invoked to show result. "); Console.Write("\n\nEnter input filename [empty for console]: "); string filename = Console.ReadLine(); // reads the filename from console StreamReader file = null; // since there is option to read data from file, opens empty StreamReader bool hasFileData = false; // by default the data should be read from console if (filename.Length > 0) // but if the content is filename (and path) { hasFileData = true; // then all input data should be read from the file try { file = new StreamReader(filename); // opens the file for reading } catch (Exception e) { Console.WriteLine("Error! ", e.ToString()); Console.WriteLine("Manual entering from console"); hasFileData = false; } } int C; int N; if (hasFileData) // reads from file { if (!int.TryParse(file.ReadLine(), out C) || C < 1 || C > 1000) { Console.WriteLine("Invalid input file: wrong number of moves"); return; }; if (!int.TryParse(file.ReadLine(), out N) || N < 1 || N > 1000) { Console.WriteLine("Invalid input file: wrong board dimension"); return; }; } else //reads from Console { do { Console.Write("\nEnter number of moves to solve, C [1,1000]: "); } while (!int.TryParse(Console.ReadLine(), out C) || C < 1 || C > 1000); do { Console.Write("\nEnter board dimensions N x N, N [2,1000]: "); } while (!int.TryParse(Console.ReadLine(), out N) || N < 2 || N > 1000); Console.WriteLine("\n\nPlease enter board data: {0} lines, {0} values per line, separated by space", N); } Playfield theBoard = new Playfield(N); // creates empty playfield for (int i = 0; i < N; i++) // iterates through rows { string[] Row; while (true) { if (hasFileData) Row = file.ReadLine().Split(' '); // reads each line from file and separates it to array of string values else { Console.Write("Row {0, 4}: ", i); Row = Console.ReadLine().Split(' '); // reads each line from console and separates it to array of string values } if (Row.Length != N) { Console.WriteLine("\nWrong number of elements in Row {0}\n", i); if (hasFileData) return; // if the input is from file stops the program continue; // else goes to start of the loop } int j; for (j = 0; j < N; j++) // iterates through cells in current row { int h = 0; if (!int.TryParse(Row[j], out h) || h < 0 || h > Tower.maxHeight) { Console.WriteLine("\nWrong element value in Row {0}, Column {1}\n", i, j); if (hasFileData) return; // if the input is from file stops the program break; // else goes out of the loop } if (h > 0 && ((j > 0 && h == theBoard.Get(i, j - 1)) || (i > 0 && h == theBoard.Get(i - 1, j)))) //checks for duplicate heights in left and upper elements { Console.WriteLine("\nDuplicate height - Element in Row {0}, Column {1}\n", i, j); if (hasFileData) return; // if the input is from file stops the program break; // else goes out of the loop } theBoard.Set(i, j, h); // and pumps up the Playfield } if (j == N) break; // correct row data - exits the while loop } } if (hasFileData) // if the data source is file { file.Close(); // closes that file file.Dispose(); // releases all used resources } Console.Write("\n\nEnter output filename [up to 10 characters, only : "); while (true) { filename = Console.ReadLine(); // reads the filename from console if (filename.Length < 1) { Console.WriteLine("\nName too short\n"); continue; } if (filename.Length > 10) { Console.WriteLine("\nName too long\n"); continue; } int i; for (i = 0; i < filename.Length; i++) if (!char.IsLetter(filename[i])) { Console.WriteLine("\nInvalid filename\n"); break; } if (i == filename.Length) break; // if filename is correct, goes out of the loop } int initScore = theBoard.Score; int maxRank = 0; // the resultng rank of the best move, initially 0 Console.WriteLine("Initial score: " + initScore); Move bestMove = new Move(); //FULL WALKTROUGH - on each level (move's depth) the best result is selected for (int depth = 0; depth < C; depth++) // iterates through possible moves { if (!theBoard.PrintBoard(filename, depth, initScore, maxRank)) return; // prints the board into HTML file, returns on error maxRank = int.MinValue; // the resultng rank of the best move, initially the minimal possible value for (int r = 0; r < N; r++) // iterates through possible rows for (int c = 0; c < N; c++) // iterates through possible colunms for (int m = 0; m < 2; m++) // iterates through possible type of moves { int diff = theBoard.NeighboursDiff(bestMove.row, bestMove.column) - theBoard.NeighboursDiff(r, c); // calculates if the neighbour difference of best move and current int rank = theBoard.Score; // gets current score of the board if (m == 0 && theBoard.Get(r, c) == 0) m = 1; // checks for invalid TAKE move, goes directly to PUT if (m == 0) rank = rank - theBoard.Take(r, c); // take = 0 and calculates the rank of the move else rank = rank - theBoard.Put(r, c); // put = 1 and calculates the rank of the move // SELECTION OF THE BEST MOVE if (rank > maxRank || // 1. (rank == maxRank && diff > 0) || // 2. and 3. (rank == maxRank && diff == 0 && theBoard.Get(r, c) >= theBoard.Get(bestMove.row, bestMove.column))) // 2. and 4. // a better move has been found if one of the following three conditons has been reached: // 1. the current rank is better than the previous one (or) // 2. the current rank is equal to previous one, but // 2.1. the difference with some neighbours is less, (or) // 3. the current rank and the differnece are equal to previous one, but // 3.1. but current height is greater { maxRank = rank; // assigns better rank of the move bestMove.type = m; // take = 0, put = 1 bestMove.row = r; // row bestMove.column = c; // column } theBoard.Resume(); // returns to previous move } // now makes the best move over all iterations of current depth if (bestMove.type == 0) { theBoard.Take(bestMove.row, bestMove.column); // take = 0 Console.Write("take "); } else { theBoard.Put(bestMove.row, bestMove.column); // put = 1 Console.Write("put "); } Console.WriteLine("{0} {1}", bestMove.row, bestMove.column); } if (!theBoard.PrintBoard(filename, -C, initScore, maxRank)) return; // prints the last board into HTML file, returns on error (index is negative in order to inform the method this is the last file) Console.WriteLine("Final score: " + theBoard.Score); Console.Write("All files generated successfully!\nPress any key to continue to browser...\n"); Console.ReadKey(true); Process.Start(filename + ".0.html"); }