0

I plan to create a command-line program with dotnet which uses a menu to get a user to decide what game they'd like to play (ive already made but not implemented tictactoe, and am trying to create an awful version of Snake) then plays them. I have three separate classes, one for the menu, one for Snake and one for Tictactoe (not implemented, not shown).

For Snake, I've tried to use System.Threading.Timer to constantly refresh the board state.

Menu Class:

using System;
using System.Threading;

namespace Boolean_Operators
{
    public class Menu
    {
        static Snake SnakeGame = new Snake();
        // var Tictactoe = new Tictactoe();

        static void Main(string[] args)
        {
            RunGame();
        }
        static void RunGame()
        {
            Timer refreshTimer = new Timer(SnakeGame.PrintBoard, null, 0, 1000);
            if(SnakeGame.invokeCount == SnakeGame.maxCount)
            {
                refreshTimer.Dispose();
            }
        }
    }
}

Snake Class:

using System;
using System.Threading;

namespace Boolean_Operators
{
    public class Snake
    {
        public int invokeCount = 0;
        public int maxCount = 20;
        string[,] board = new string[,]{
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",},
            {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ",}
        };
        

        public void PrintBoard(object state)
        {
            invokeCount++;
            for(int i = 0; i <= 10; i++)
            {
                Console.BackgroundColor = ConsoleColor.DarkGreen;
                Console.Write($"  {board[i, 0]}  |  {board[i, 1]}  |  {board[i, 2]}  |  {board[i, 3]}  |  {board[i, 4]}  |  {board[i, 5]}  |  {board[i, 6]}  |  {board[i, 7]}  |  {board[i, 8]}  |  {board[i, 9]}  ");
                Console.BackgroundColor = ConsoleColor.Black;
                Console.Write("\n");
                Console.BackgroundColor = ConsoleColor.DarkGreen;
                Console.Write("-----+-----+-----+-----+-----+-----+-----+-----+-----+-----");
                Console.BackgroundColor = ConsoleColor.Black;
                Console.Write("\n");
            }
            Console.BackgroundColor = ConsoleColor.DarkGreen;
            Console.Write($"  {board[9, 0]}  |  {board[9, 1]}  |  {board[9, 2]}  |  {board[9, 3]}  |  {board[9, 4]}  |  {board[9, 5]}  |  {board[9, 6]}  |  {board[9, 7]}  |  {board[9, 8]}  |  {board[9, 9]}  ");
            Console.BackgroundColor = ConsoleColor.Black;
            Console.Write("\n");
        }
    }
    
}

For whatever reason, all of the code appears to execute however the PrintBoard method code never executes, and when running the program nothing occurs:

Console Output when running

I'm hoping the answer is something blindingly obvious I've missed since I've tried a lot of garbage stuff to try to get around it.

  • Im not sure but after testing the code for a bit I think the main thread finished executing before the timer callback had it chance to run, you can see that if you add a small sleep of 2ms after the timer creation, the board will be printed, Hope that will help you solve the problem. – Daniel Botnik Jul 24 '21 at 16:50

1 Answers1

0

In RunGame these are the steps:

  1. Create a new timer
  2. 0-N timer callbacks (probably 0).
  3. Evaluate the if statement after that, which will be false.
  4. 0-N timer callbacks (probably 0).
  5. Exit program

Your timer never has a chance to invoke, or if it does, it is only invoked just a few times before the program exits.

You have to wait inside your Main() after step #1. The Timer class documentation shows you a way to do that using AutoResetEvent. Another way you could do that is to do a SpinWait.SpinUntil call. You could change your code to

static void RunGame()
{
    Timer refreshTimer = new Timer(SnakeGame.PrintBoard, null, 0, 1000);
    SpinWait.SpinUntil(() => SnakeGame.invokeCount == SnakeGame.maxCount);
    refreshTimer.Dispose();
}

This blocks Main() until the condition is met.

Kit
  • 20,354
  • 4
  • 60
  • 103