Example #1
0
        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);
                }
            }
        }
Example #2
0
        //internal function in help of Put and Take
        private int CheckNeighbours(int r, int c)
        {
            int result = 0;
            int currentHeight = Board[r, c].Get();
            if (currentHeight == 0) return 0; // no height in the current cell, no need to check neighbours
            Move toPush = new Move(); // saving board change into the stack

            if (r > 0 && Board[r - 1, c].Get() == currentHeight) // checks upper element (if any)
            {
                Board[r - 1, c].Destroy();
                result += currentHeight;
                toPush.type = -currentHeight; //Negative type means height of a tower
                toPush.row = r - 1; //Row
                toPush.column = c; //Col
                OldMoves.Push(toPush);
            }
            if (r < (Board.GetLength(0) - 1) && Board[r + 1, c].Get() == currentHeight) // checks lower element (if any)
            {
                Board[r + 1, c].Destroy();
                result += currentHeight;
                toPush.type = -currentHeight; //Negative type means height of a tower
                toPush.row = r + 1; //Row
                toPush.column = c; //Col
                OldMoves.Push(toPush);
            }

            if (c > 0 && Board[r, c - 1].Get() == currentHeight) // checks left element (if any)
            {
                Board[r, c - 1].Destroy();
                result += currentHeight;
                toPush.type = -currentHeight; //Negative type means height of a tower
                toPush.row = r; //Row
                toPush.column = c - 1; //Col
                OldMoves.Push(toPush);
            }
            if (c < (Board.GetLength(1) - 1) && Board[r, c + 1].Get() == currentHeight) // checks right element (if any)
            {
                Board[r, c + 1].Destroy();
                result += currentHeight;
                toPush.type = -currentHeight; //Negative type means height of a tower
                toPush.row = r; //Row
                toPush.column = c + 1; //Col
                OldMoves.Push(toPush);
            }

            if (result > 0) // if there is some destroy operation the current cell also has to be destroyed
            {
                Board[r, c].Destroy();
                result += currentHeight;
                toPush.type = -currentHeight; //Negative type means height of a tower
                toPush.row = r; //Row
                toPush.column = c; //Col
                OldMoves.Push(toPush);
            }

            return result;
        }
Example #3
0
 public int Take(int r, int c)
 {
     Board[r, c]--; // decrement the indexed tower
     Score--; // decrement the score
     Move toPush = new Move(); //puts the move into the stack
     toPush.type = 0; //Take
     toPush.row = r; //Row
     toPush.column = c; //Col
     OldMoves.Push(toPush);
     Score -= CheckNeighbours(r, c); // checks for equal neighbour towers and returns the quantity of destroyed pieces
     return Score;
 }
Example #4
0
 public void Resume()
 {
     Move toPop = new Move();
     while (true)
     {
         toPop = OldMoves.Pop();
         if (toPop.type == 0) // if the stacked move was TAKE, we must perform PUT operation
         {
             Set(toPop.row, toPop.column, Get(toPop.row, toPop.column) + 1); // increases the height by 1
             return; // out of the method
         }
         if (toPop.type == 1) //if the stacked move is PUT, we must perform TAKE operation
         {
             Set(toPop.row, toPop.column, Get(toPop.row, toPop.column) - 1); // if could be done, decreases the height by 1
             return; // out of the method
         }
         // otherwise loops until take or put method is found
         Set(toPop.row, toPop.column, -toPop.type); // sets the column height with inverted sign
     }
 }
Example #5
0
        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");
        }
Example #6
0
        public bool PrintBoard(string filename, int index, int initScore, int rank)
        {
            StreamWriter outFile = null; // since the output is file, opens empty StreamWriter

            string next = "#"; // initially there is no next move
            if (index < 0) index = -index; // cheat to inform the method this will be the last file
            else next = filename + "." + (index + 1) + ".html"; // previous move filename
            string previous = "#"; // initially there is no previous move
            if (index > 0) previous = filename + "." + (index - 1) + ".html"; // next move filename
            filename = filename + "." + index + ".html"; // combines the full filename
            Move[] lastMoves = new Move[5];
            lastMoves[0].row = lastMoves[1].row = lastMoves[2].row = lastMoves[3].row = lastMoves[4].row = -1; // guarantees initial out of board
            if (index > 0) // if we have some moves, the stack is not empty
            {
                int i;
                for (i = 0; i < 5; i++) // for the current move in the stack we have at least 1 move and not more than 5 (1 move + 4 neighbours)
                {
                    lastMoves[0] = OldMoves.Pop(); // extracts the lsst move from stack
                    if (lastMoves[0].type < 0) lastMoves[i+1] = lastMoves[0]; // this move is a result of neighbours destroy, move in the tail
                    else break; // since the exact move is pushed first into the stack
                }
                for (; i >= 0; i--) //returns back moves into the stack
                    OldMoves.Push(lastMoves[i]);
            } //now we have in lastMoves[0] our last move and in some of lastMoves[1]..lastMoves[4] our last neighbour moves

            try
            {
                outFile = new StreamWriter(filename); // opens the file for writing
            }
            catch (Exception e)
            {
                Console.WriteLine("Error! ", e.ToString());
                Console.WriteLine("Please restart the application");
                return false;
            }
            outFile.Write(@"
            <html>
            <head>
            <title>Trolls Game Visualizer</title>
            <link rel=""stylesheet"" type=""text/css"" href=""styles.css"">
            </head>
            <body>
            <div class=""button-wrapper"">");
            if(previous.Length > 1) outFile.Write(@"
            <a href=""{0}"">Previous move</a>", previous);
            outFile.Write(@"
            </div>
            <div id=""next_div"" class=""button-wrapper"">");
            if(next.Length > 1) outFile.Write(@"
            <a id=""next"" href=""{0}"">Next move</a>", next);
            outFile.Write(@"
            </div>
            <h1>Trolls Game Visualizer</h1>
            <h2>Move {0}</h2>
            <h3>",index);
            if (index>0)
            {
                if (lastMoves[0].type == 0) outFile.Write("TAKE");
                else outFile.Write("PUT");
                outFile.Write(" {0} {1}", lastMoves[0].row, lastMoves[0].column);
            } else outFile.Write("Initial content");
            outFile.Write(@"</h3>
            <div id=""container"">
            <table id=""Board"">
                <tr>
                    <td class=""head"">&nbsp;</td>");
            for (int i = 0; i < Board.GetLength(0); i++)
            {
                outFile.Write(@"
                    <td class=""head"">{0}</td>", i);
            }
            outFile.Write(@"
                </tr>");
            for (int r = 0; r < Board.GetLength(0); r++)
            {
            outFile.Write(@"
                <tr>
                    <td class=""head"">{0}</td>", r);
                for (int c = 0; c < Board.GetLength(1); c++)
                {
                    string moveType="";
                    if (lastMoves[0].row == r && lastMoves[0].column == c) moveType = " class=\"move\"";
                    else
                        for (int i = 1; i < 5; i++)
                            if (lastMoves[i].row == r && lastMoves[i].column == c) moveType = " class=\"neighbour\"";
                     outFile.Write(@"
                    <td{1}>{0}</td>", Get(r, c), moveType);
                }
                outFile.Write(@"
                </tr>");
            }
            outFile.Write(@"
            </table>
            <p><span class=""move"">&nbsp;&nbsp;&nbsp;&nbsp;</span> - the move's cell,
               <span class=""neighbour"">&nbsp;&nbsp;&nbsp;&nbsp;</span> - the destroyed neighbours cells</p>
            <table>
                <tr>
                    <td>Initial board score:</td>
                    <td>{0}</td>
                    <td>&nbsp;&nbsp;</td>
                    <td>Current board score:</td>
                    <td>{1}</td>
                </tr>
                <tr>
                    <td>Points, earned to the moment:</td>
                    <td>{2}</td>
                    <td>&nbsp;&nbsp;</td>
                    <td>Points, earned at current move:</td>
                    <td>{3}</td>
                </tr>
            </table>
            </div>
            <hr />
            <h4>author: Pavel Sotirov</h4>
            </body>
            </html>
            ", initScore, Score, initScore - Score, rank);
            outFile.Close(); // closes that file
            outFile.Dispose(); // releases all used resources
            return true;
        }