이제는 코드 작업.
1. 멤버 변수 선언

solution explorer에서 Form1.cs를 선택하고 표시된 아이콘을 클릭하면 Form1.cs 소스 화일을 보여준다.
이 에디터에서 작업을 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // dimension of each cell in the grid
const int CellWidth = 32;
const int CellHeight = 32;
// offset from the top-left corner of the window
const int xOffset = -20;
const int yOffset = 25;
// color for empty cells
private Color DEFAULT_BACKCOLOR = Color.White;
// color for original puzzle values
private Color FIXED_FORECOLOR = Color.Blue;
private Color FIXED_BACKCOLOR = Color.LightSteelBlue;
// color for user inserted values
private Color USER_FORECOLOR = Color.Black;
private Color USER_BACKCOLOR = Color.LightYellow;
// the number currently selected for insertion
private int SelectedNumber;
// stacks to keep track of all the moves
// private Stack Moves = new Stack(); // 이 표현도 괜찮은 듯
private Stack<string> Moves = new Stack<string>(); // generic type?
private Stack<string> RedoMoves = new Stack<string>();
// keep track of filename to save to
private String saveFileName = String.Empty;
// used to represent the values in the grid
private int [,] actual = new int[10, 10];
// used to keep track of elapsed time
private int seconds = 0;
// has the game started?
private Boolean GameStarted = false;
|
위 코드를 class Form1안에 추가한다.
2. 그리드(셀) 값의 표현
아래 그림과 같은 값을 표현할 때에는,

actual(1, 1) = 4
actual(2, 1) = 0
actual(3, 1) = 2
actual(4, 1) = 0
actual(5, 1) = 3
...
actual(1, 2) = 7
...
이렇게 표현한다. 따라서 10x10 배열이 필요하고, (0,x), (x, 0) 은 사용하지 않는다.
3. 셀의 이름
각 셀은 동적으로 생성된 Label control로 나타내어진다. 각 Label의 Name 프라퍼티는 column과 row의 조합으로 구성된다. 즉,
cell (1, 1) => 11
cell (2, 1) => 21
...
이렇게 표현된다.
4. Erasability of a Cell
각 셀은 유저가 입력한 값(value)나 퍼즐의 고유한 값이 입력된다. 유저가 입력한 값은 지울 수 있고, 퍼즐 고유의 값은 지울 수 없다. 이 특성은 Label의 Tag 프라퍼티를 이용하여 구현한다.
Label.Tag = "1" // value can be erased
Label.Tag = "0" // cannot be erased
5. 스택에 Moves 저장하기
셀에 값이 입력 될 때마다 스택에 좌표와 값을 저장한다. 유저가 undo를 할 경우 Moves 스택에서 값을 pop하여 Redo 스택에 push 하여 저장한다. 유저가 redo를 선택하면 redo 스택에서 값을 pop 하여 Moves 스택에 push 한다. 스택에 저장되는 값은 세자리의 숫자로 이루어진 스트링이다. (1, 3)에 값 5 => "135" 이렇게 표현된다.

6. 그리드의 동적 생성
어플리케이션이 실행될 때 처음으로 할 일은 9x9, 총 81개의 그리드를 생성하는 일이다. 다음의 코드를 Form1 class 안에 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public void DrawBoard()
{
// default selected number is 1
toolStripButton1.Checked = true;
SelectedNumber = 1;
// used to store the location of the cell
Point location = new Point();
for ( int row = 1; row < 10; row++ )
{
for ( int col = 1; col < 10; col++ )
{
location.X = col * ( CellWidth + 1 ) + xOffset;
location.Y = row * ( CellHeight + 1 ) + yOffset;
Label lbl = new Label();
lbl.Name = col.ToString() + row.ToString();
lbl.BorderStyle = BorderStyle.None;
lbl.Location = location;
lbl.Width = CellWidth;
lbl.Height = CellHeight;
lbl.TextAlign = ContentAlignment.MiddleCenter;
lbl.BackColor = DEFAULT_BACKCOLOR;
lbl.Font = new Font( lbl.Font, lbl.Font.Style | FontStyle.Bold );
lbl.Tag = "1";
// Add Handler
lbl.Click += new EventHandler( Cell_Click );
this.Controls.Add( lbl );
}
}
}
|
lbl.Click += new EventHandler( Cell_Click ) 에서 각 Label이 마우스 클릭 이벤트가 발생할 때마다 Cell_Click을 실행하라는 이벤트핸들러를 등록하는 것을 눈여겨 본다. 그리고 this.Controls.Add( lbl ) 로 생성된 Label control을 Form1 (this) 에 추가시킨다.
Cell_Click() 이 없어서 빌드가 되지 않으니 일단 아래의 코드를 추가하여 둔다.
1 2 3 4 5 | // event handler for cell click
private void Cell_Click( object sender, EventArgs e )
{
return;
}
|
빌드를 하고 실행을 하여보면 그리드가 그려지지 않는다. 그리드를 그리는 DraqBoard()가 실행되지 않았기 때문. 초기화는 Form1이 실행될 때 발생하는 이벤트인 Form1_Load()에서 한다. Form1.cs [Design]에서 폼을 더블클릭하면 숨겨져 있던 Form1_Load() 코드를 구현해준다. 다음과 같이 수정한다.
1 2 3 4 5 6 7 8 9 | private void Form1_Load( object sender, EventArgs e )
{
// initialize the status bar
toolStripStatusLabel1.Text = String.Empty;
toolStripStatusLabel2.Text = String.Empty;
// draw the board
DrawBoard();
}
|
다시 Design 창에가서 Form1을 선택하고 프라퍼티 창을 보면 다음과 같이 이벤트 Load에 대한 핸들러를 확인 할 수 있다. 이런 구조를 잘 기억한다.

다시 빌드하고 실행을 해보면 그리드가 그려진다. 3x3 의 minigrid의 outline을 그린다. Form1의 Paint 이벤트 핸들러를 이용하여 그린다. 아래 그림처럼 Form1의 프라퍼티 창, 이벤트 뷰에서 Paint 항목을 찾고, 더블 클릭한다. 그러면 자동으로 Form1_Paint() 이벤트 핸들러 코드를 생성한다.

아래와 같이 Form1_Paint()를 수정한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | //
// draw the lines outlining the minigrids
//
private void Form1_Paint( object sender, PaintEventArgs e )
{
int x1, y1, x2, y2;
// draw the horizontal lines
x1 = 1 * ( CellWidth + 1 ) + xOffset - 1;
x2 = 9 * ( CellWidth + 1 ) + xOffset + CellWidth;
for ( int i = 1; i <= 10; i = i + 3 )
{
y1 = i * ( CellHeight + 1 ) + yOffset - 1;
y2 = y1;
e.Graphics.DrawLine( Pens.Black, x1, y1, x2, y2 );
}
// draw the vertical lines
y1 = 1 * ( CellHeight + 1 ) + yOffset - 1;
y2 = 9 * ( CellHeight + 1 ) + yOffset + CellHeight;
for ( int j = 1; j <= 10; j += 3 )
{
x1 = j * ( CellWidth + 1 ) + xOffset - 1;
x2 = x1;
e.Graphics.DrawLine( Pens.Black, x1, y1, x2, y2 );
}
}
|
빌드를 하고 실행을 하면 아래와 같은 모습을 볼 수 있다.
