C#控制台实现倒计时
原文地址:https://www.cnblogs.com/jingjiangtao/p/14766049.html
效果
文章最后有完整代码,以下是逐步实现。
准备工作
要在控制台上绘制出数字,可以将控制台看作一个网格,在网格中填充颜色就可以显示不同的数字。首先在excel中做一个网格来确定大小和坐标:
可以看到,画布大小是 41 x 11,组成每个数字的网格也都标出了坐标,程序中需要绘制数字时只需要参照这个excel网格就行了。
关于如何将excel的表格修改成正方形,可以参考这篇博客:使用Excel在图片上添加正方形网格线_zy_like_study的博客-CSDN博客_图片添加网格线
开始编写代码
创建网格
新建一个 .net 5 控制台项目。因为功能简单,所以就直接在默认的Program类中用静态函数实现了。
首先要定义一个 41 x 11 的网格用来展示数字。新建一个类 Cell 表示网格中的每一个格子,Value属性表示这个格子是否激活,true为激活,false为未激活。
public class Cell { public bool Value { get; set; } }
接下来要定义网格,可以用一个二维数组来表示,数组的类型就是上面定义的Cell。将这个二维数组定义为类的静态变量,就可以在类的静态函数中使用。
private static Cell[,] grid = new Cell[11, 41];
定义完成之后需要初始化网格,对二维数组中的类型进行实例化。Init() 函数遍历网格数组,实例化每个格子,并显式将Value设为false
private static void Init() { for (int i = 0; i < grid.GetLength(0); i++) { for (int j = 0; j < grid.GetLength(1); j++) { grid[i, j] = new Cell(); grid[i, j].Value = false; } } }
在控制台上显示网格
现在有了数据,接下来就要将数据展示在控制台上。update() 函数遍历网格数组,判断每个格子的Value值,如果为true,就将格子设为白色,如果为false,就将格子设为黑色,其中白色表示激活,黑色表示未激活。
private static void Update() { for (int i = 0; i < grid.GetLength(0); i++) { for (int j = 0; j < grid.GetLength(1); j++) { if (grid[i, j].Value) { SetWhite(i, j); } else { SetBlack(i, j); } } } }
SetWhite()函数和SetBlack()函数调用了C#控制台的接口,将指定位置的背景设为指定颜色。
private static void SetWhite(int i, int j) { string fill = " "; // 两个空格 Console.SetCursorPosition(j * fill.Length, i); Console.BackgroundColor = ConsoleColor.White; Console.Write(fill); } private static void SetBlack(int i, int j) { string fill = " "; // 两个空格 Console.SetCursorPosition(j * fill.Length, i); Console.BackgroundColor = ConsoleColor.Black; Console.Write(fill); }
至此,网格的创建和显示就完成了,不过这两个函数还没有被调用。
在网格上绘制数字
现在网格可以通过update()函数显示到控制台上了,接下来就可以对照那张excel表绘制指定的数字了。我用的这个绘制数字的方式是比较笨的,没有想出来更好的办法,如果有,欢迎在评论区指出。
绘制的基本方法就是把组成数字的格子设成白色,没有数字的格子设成黑色,0到9一共10个数字全都绘制一遍。
先是一个枚举,表示要绘制的数字开始的列,其中HourFirst表示“小时”的十位,开始于网格第1列,HourSecond表示“小时”的个位,开始于网格的第7列,依此类推。
public enum NumberLocation { HourFirst = 1, HourSecond = 7, MinuteFirst = 15, MinuteSecond = 21, SecondFirst = 29, SecondSecond = 35, }
下面是绘制代码,因为太长就默认折叠了。其中TraverseNumber函数就是为了复用二重循环。DisplayNumber函数根据每个数字的坐标设置格子的Value值,location参数表示要显示数字的列位置,接收一个枚举,比如 DisplayNumber(3, NumberLocation.HourFirst) 表示“小时”的十位是3。
private static void DisplayNumber(int number, NumberLocation location) { int colOffset = (int)location; switch (number) { case 0: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4 || i == 5 || i == 6 || i == 7 || i == 8) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 1: TraverseNumber(colOffset, (i, j) => { if (j == colOffset + 4) { grid[i, j].Value = true; return; } grid[i, j].Value = false; }); break; case 2: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8) { if (j == colOffset) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 3: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4 || i == 6 || i == 7 || i == 8) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 4: TraverseNumber(colOffset, (i, j) => { if (i == 5) { grid[i, j].Value = true; return; } if (i == 1 || i == 2 || i == 3 || i == 4) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8 || i == 9) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 5: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4) { if (j == colOffset) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 6: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4) { if (j == colOffset) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 7: TraverseNumber(colOffset, (i, j) => { if (i == 1) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4 || i == 5 || i == 6 || i == 7 || i == 8 || i == 9) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 8: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4 || i == 6 || i == 7 || i == 8) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 9: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; } }
利用这个函数,就可以在控制台上显示数字了,修改Main函数:
static void Main(string[] args) { Init(); DisplayNumber(1, NumberLocation.HourFirst); DisplayNumber(2, NumberLocation.HourSecond); DisplayNumber(3, NumberLocation.MinuteFirst); DisplayNumber(4, NumberLocation.MinuteSecond); DisplayNumber(5, NumberLocation.SecondFirst); DisplayNumber(6, NumberLocation.SecondSecond); Update(); }
但是现在只有数字,没有分隔 小时 : 分钟 : 秒 的那两个小点。接下来绘制分隔点。首先定义枚举类,确定分隔点所在的列:
public enum DotLocation { First = 13, Second = 27 }
然后显示分隔点:
private static void ShowAllDot() { int firstCol = (int)DotLocation.First; int secondCol = (int)DotLocation.Second; grid[3, firstCol].Value = true; SetWhite(3, firstCol); grid[7, firstCol].Value = true; SetWhite(7, firstCol); grid[3, secondCol].Value = true; SetWhite(3, secondCol); grid[7, secondCol].Value = true; SetWhite(7, secondCol); }
在Main函数中调用:
static void Main(string[] args) { Init(); DisplayNumber(1, NumberLocation.HourFirst); DisplayNumber(2, NumberLocation.HourSecond); DisplayNumber(3, NumberLocation.MinuteFirst); DisplayNumber(4, NumberLocation.MinuteSecond); DisplayNumber(5, NumberLocation.SecondFirst); DisplayNumber(6, NumberLocation.SecondSecond); ShowAllDot(); Update(); }
运行看效果:
至此,数字和分隔点可以正常显示了。
开始倒计时
完成了设置数据和显示数据的功能,就可以开始实现倒计时功能了。实现倒计时需要计时库,C#提供了Stopwatch用来精确计时,我们就用这个类。
一开始先把倒计时的时间硬编码,比如倒计时70秒,完成主要功能后再考虑让用户输入。LeftTime函数将倒计时的总时间和经过的时间相减,再调用DisplayNumber修改网格的值。
修改Main函数。Main函数定义了倒计时时间,初始化网格,再定义Stopwatch变量,之后是一个无限循环。循环过程中用Stopwatch计时,如果经过时间大于倒计时总时间,则说明计时时间到。每次循环都调用Update函数来显示数字:
static void Main(string[] args) { int countdownSec = 70; Init(); Stopwatch stopwatch = null; while (true) { if (stopwatch == null) { stopwatch = Stopwatch.StartNew(); } // 时间到 if (stopwatch != null && stopwatch.Elapsed.TotalSeconds > countdownSec) { LeftTime(countdownSec, countdownSec); stopwatch.Stop(); stopwatch = null; } else { LeftTime(countdownSec, (int)stopwatch.Elapsed.TotalSeconds); } Update(); } } private static void LeftTime(int countdownSec, int elapsedSec) { var timeLeft = TimeSpan.FromSeconds(countdownSec - elapsedSec); DisplayNumber(timeLeft.Hours / 10, NumberLocation.HourFirst); DisplayNumber(timeLeft.Hours % 10, NumberLocation.HourSecond); DisplayNumber(timeLeft.Minutes / 10, NumberLocation.MinuteFirst); DisplayNumber(timeLeft.Minutes % 10, NumberLocation.MinuteSecond); DisplayNumber(timeLeft.Seconds / 10, NumberLocation.SecondFirst); DisplayNumber(timeLeft.Seconds % 10, NumberLocation.SecondSecond); }
运行一下:
可以看到计时器开始正常运行了。但是发现了一个问题,没有分隔点。分隔点都是闪烁的,不是静止的,所以需要添加一个切换分隔点状态的函数,每次调用这个函数,分隔点都会切换显示或隐藏的状态:
private static void SwitchDot(DotLocation location) { int colOffset = (int)location; if (grid[3, colOffset].Value) { grid[3, colOffset].Value = false; SetBlack(3, colOffset); } else { grid[3, colOffset].Value = true; SetWhite(3, colOffset); } if (grid[7, colOffset].Value) { grid[7, colOffset].Value = false; SetBlack(7, colOffset); } else { grid[7, colOffset].Value = true; SetWhite(7, colOffset); } }
可以让分隔点每500ms切换一次状态,方法和倒计时的方法相同。为了不让分隔点的计算影响倒计时的计算,需要单独开一个线程。这里用Thread开线程,也可以用Task.Run开线程。修改Main函数:
static void Main(string[] args) { int countdownSec = 70; Init(); Thread dotThread = new Thread(() => { ShowAllDot(); Stopwatch dotWatch = null; while (true) { if (dotWatch == null) { dotWatch = Stopwatch.StartNew(); } if (dotWatch.ElapsedMilliseconds >= 500) { SwitchDot(DotLocation.First); SwitchDot(DotLocation.Second); dotWatch.Stop(); dotWatch = null; } } }); dotThread.Start(); Stopwatch stopwatch = null; while (true) { if (stopwatch == null) { stopwatch = Stopwatch.StartNew(); } // 时间到 if (stopwatch != null && stopwatch.Elapsed.TotalSeconds > countdownSec) { LeftTime(countdownSec, countdownSec); stopwatch.Stop(); stopwatch = null; } else { LeftTime(countdownSec, (int)stopwatch.Elapsed.TotalSeconds); } Update(); } }
运行一下:
可以看到分隔点闪烁起来了。
但是现在的这个计时器不会停止,等计时完成后会重新开始,所以我们要让计时完成时停止计时,并让分隔点静止。设置一个bool变量isCounting表示是否正在计时,为true表示正在计时,为false表示计时完毕。
修改Main函数:
static void Main(string[] args) { int countdownSec = 5; Init(); bool isCounting = true; Thread dotThread = new Thread(() => { ShowAllDot(); Stopwatch dotWatch = null; while (true) { if (dotWatch == null && isCounting) { dotWatch = Stopwatch.StartNew(); } if (isCounting && dotWatch.ElapsedMilliseconds >= 500) { SwitchDot(DotLocation.First); SwitchDot(DotLocation.Second); dotWatch.Stop(); dotWatch = null; } } }); dotThread.Start(); Stopwatch stopwatch = null; while (true) { if (stopwatch == null && isCounting) { stopwatch = Stopwatch.StartNew(); } // 时间到 if (stopwatch != null && stopwatch.Elapsed.TotalSeconds > countdownSec) { LeftTime(countdownSec, countdownSec); ShowAllDot(); stopwatch.Stop(); stopwatch = null; isCounting = false; } else if(isCounting) { LeftTime(countdownSec, (int)stopwatch.Elapsed.TotalSeconds); } Update(); } }
为了尽快看到效果,把计时总时间改为5秒。运行一下:
可以看到计时完成之后数字不再改变,分隔点也静止了。
至此,倒计时的主要功能就完成了。
用户输入
作为一个倒计时工具,把倒计时时间硬编码肯定是不行的,需要让用户输入,还有退出倒计时页面、隐藏游标等功能。修改Main函数:
static void Main(string[] args) { while (true) { Console.Clear(); Console.CursorVisible = true; bool isQuit = false; bool isCounting = false; int countdownSec = 0; while (true) { Console.Write("Input Countdown Seconds: "); string countdownSecStr = Console.ReadLine(); if (int.TryParse(countdownSecStr, out countdownSec)) { break; } } Init(); Console.CursorVisible = false; isCounting = true; Thread dotThread = new Thread(() => { ShowAllDot(); Stopwatch dotWatch = null; while (true) { if (isQuit) { break; } if (dotWatch == null && isCounting) { dotWatch = Stopwatch.StartNew(); } if (isCounting && dotWatch.ElapsedMilliseconds >= 500) { SwitchDot(DotLocation.First); SwitchDot(DotLocation.Second); dotWatch.Stop(); dotWatch = null; } } }); dotThread.Start(); Thread quitThread = new Thread(() => { ConsoleKeyInfo key = Console.ReadKey(); if (key.Key == ConsoleKey.Q) { isQuit = true; } }); quitThread.Start(); Stopwatch stopwatch = null; while (true) { if (isQuit) { break; } if (stopwatch == null && isCounting) { stopwatch = Stopwatch.StartNew(); } // 时间到 if (stopwatch != null && stopwatch.Elapsed.TotalSeconds > countdownSec) { LeftTime(countdownSec, countdownSec); ShowAllDot(); stopwatch.Stop(); stopwatch = null; isCounting = false; } else if (isCounting) { LeftTime(countdownSec, (int)stopwatch.Elapsed.TotalSeconds); } Update(); Console.WriteLine("\n Press q to quit"); } } }
最外层是一个无限循环,以保证程序不会自己退出。循环刚开始就接收用户输入的以秒为单位的总时间,之后初始化网格,隐藏游标,设置标志变量,开启线程等。quitThread线程监听用户是否按了q键,是就退出倒计时页面,返回到输入页面。
完整代码
完整代码将Main函数中的所有操作提取到了Countdown函数中,Main函数调用Countdown函数。
using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace Countdown { class Program { private static Cell[,] grid = new Cell[11, 41]; static void Main(string[] args) { Countdown(); } private static void Countdown() { while (true) { Console.Clear(); Console.CursorVisible = true; bool isQuit = false; bool isCounting = false; int countdownSec = 0; while (true) { Console.Write("Input Countdown Seconds: "); string countdownSecStr = Console.ReadLine(); if (int.TryParse(countdownSecStr, out countdownSec)) { if (countdownSec > 86400) { continue; } break; } } Init(); Console.CursorVisible = false; isCounting = true; Thread dotThread = new Thread(() => { ShowAllDot(); Stopwatch dotWatch = null; while (true) { if (isQuit) { break; } if (dotWatch == null && isCounting) { dotWatch = Stopwatch.StartNew(); } if (isCounting && dotWatch.ElapsedMilliseconds >= 500) { SwitchDot(DotLocation.First); SwitchDot(DotLocation.Second); dotWatch.Stop(); dotWatch = null; } } }); dotThread.Start(); Thread quitThread = new Thread(() => { ConsoleKeyInfo key = Console.ReadKey(); if (key.Key == ConsoleKey.Q) { isQuit = true; } }); quitThread.Start(); Stopwatch stopwatch = null; while (true) { if (isQuit) { break; } if (stopwatch == null && isCounting) { stopwatch = Stopwatch.StartNew(); } // 时间到 if (stopwatch != null && stopwatch.Elapsed.TotalSeconds > countdownSec) { LeftTime(countdownSec, countdownSec); ShowAllDot(); stopwatch.Stop(); stopwatch = null; isCounting = false; } else if (isCounting) { LeftTime(countdownSec, (int)stopwatch.Elapsed.TotalSeconds); } Update(); Console.WriteLine("\n Press q to quit"); } } } private static void LeftTime(int countdownSec, int elapsedSec) { var timeLeft = TimeSpan.FromSeconds(countdownSec - elapsedSec); DisplayNumber(timeLeft.Hours / 10, NumberLocation.HourFirst); DisplayNumber(timeLeft.Hours % 10, NumberLocation.HourSecond); DisplayNumber(timeLeft.Minutes / 10, NumberLocation.MinuteFirst); DisplayNumber(timeLeft.Minutes % 10, NumberLocation.MinuteSecond); DisplayNumber(timeLeft.Seconds / 10, NumberLocation.SecondFirst); DisplayNumber(timeLeft.Seconds % 10, NumberLocation.SecondSecond); } private static void Init() { for (int i = 0; i < grid.GetLength(0); i++) { for (int j = 0; j < grid.GetLength(1); j++) { grid[i, j] = new Cell(); grid[i, j].Value = false; } } } private static void Update() { for (int i = 0; i < grid.GetLength(0); i++) { for (int j = 0; j < grid.GetLength(1); j++) { if (grid[i, j].Value) { SetWhite(i, j); } else { SetBlack(i, j); } } } } private static void SetWhite(int i, int j) { string fill = " "; Console.SetCursorPosition(j * fill.Length, i); Console.BackgroundColor = ConsoleColor.White; Console.Write(fill); } private static void SetBlack(int i, int j) { string fill = " "; Console.SetCursorPosition(j * fill.Length, i); Console.BackgroundColor = ConsoleColor.Black; Console.Write(fill); } private static void DisplayNumber(int number, NumberLocation location) { int colOffset = (int)location; switch (number) { case 0: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4 || i == 5 || i == 6 || i == 7 || i == 8) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 1: TraverseNumber(colOffset, (i, j) => { if (j == colOffset + 4) { grid[i, j].Value = true; return; } grid[i, j].Value = false; }); break; case 2: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8) { if (j == colOffset) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 3: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4 || i == 6 || i == 7 || i == 8) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 4: TraverseNumber(colOffset, (i, j) => { if (i == 5) { grid[i, j].Value = true; return; } if (i == 1 || i == 2 || i == 3 || i == 4) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8 || i == 9) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 5: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4) { if (j == colOffset) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 6: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4) { if (j == colOffset) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 7: TraverseNumber(colOffset, (i, j) => { if (i == 1) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4 || i == 5 || i == 6 || i == 7 || i == 8 || i == 9) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 8: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4 || i == 6 || i == 7 || i == 8) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; case 9: TraverseNumber(colOffset, (i, j) => { if (i == 1 || i == 5 || i == 9) { grid[i, j].Value = true; return; } if (i == 2 || i == 3 || i == 4) { if (j == colOffset || j == colOffset + 4) { grid[i, j].Value = true; return; } } if (i == 6 || i == 7 || i == 8) { if (j == colOffset + 4) { grid[i, j].Value = true; return; } } grid[i, j].Value = false; }); break; } } private static void TraverseNumber(int colOffset, Action<int, int> action) { for (int i = 1; i < 10; i++) { for (int j = colOffset; j < colOffset + 5; j++) { action(i, j); } } } private static void SwitchDot(DotLocation location) { int colOffset = (int)location; if (grid[3, colOffset].Value) { grid[3, colOffset].Value = false; SetBlack(3, colOffset); } else { grid[3, colOffset].Value = true; SetWhite(3, colOffset); } if (grid[7, colOffset].Value) { grid[7, colOffset].Value = false; SetBlack(7, colOffset); } else { grid[7, colOffset].Value = true; SetWhite(7, colOffset); } } private static void ShowAllDot() { int firstCol = (int)DotLocation.First; int secondCol = (int)DotLocation.Second; grid[3, firstCol].Value = true; SetWhite(3, firstCol); grid[7, firstCol].Value = true; SetWhite(7, firstCol); grid[3, secondCol].Value = true; SetWhite(3, secondCol); grid[7, secondCol].Value = true; SetWhite(7, secondCol); } private static void HideAllDot() { int firstCol = (int)DotLocation.First; int secondCol = (int)DotLocation.Second; grid[3, firstCol].Value = false; SetBlack(3, firstCol); grid[7, firstCol].Value = false; SetBlack(7, firstCol); grid[3, secondCol].Value = false; SetBlack(3, secondCol); grid[7, secondCol].Value = false; SetBlack(7, secondCol); } } public class Cell { public bool Value { get; set; } } public enum NumberLocation { HourFirst = 1, HourSecond = 7, MinuteFirst = 15, MinuteSecond = 21, SecondFirst = 29, SecondSecond = 35, } public enum DotLocation { First = 13, Second = 27 } }