이제는 코드 작업.


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 );
    }
}


빌드를 하고 실행을 하면 아래와 같은 모습을 볼 수 있다.





Posted by 쿨한넘