Delphi – 숫자야구 게임

이번에는 간단한 숫자야구를 제작해 보도록 하겠습니다.

숫자야구는 학창 시절에 친구들과 해보신 기억이 있으실 겁니다. 규칙은 간단한데 두 사람이 서로 세자리 수를 중복 없이 정하고 번갈아가며 세자리 숫자를 제시하면 상대방이 자신이 정한 숫자를 보고 Strike와 Ball을 판정해 줍니다. 숫자가 있는 경우에만 Strike와 Ball이 되고 아예 없으면 Out이 됩니다.

숫자가 있으면서 위치가 같으면 Strike, 위치가 같지 않으면 Ball 입니다. 예를 들어 보도록 하겠습니다.

A와 B가 게임을 한다고 가정하고 A는 587을 정했고 B는 698을 정했습니다. 먼저 A가 B에게 123을 제시합니다. B는 자신이 정한 숫자에 123이 들어있지 않으므로 Out이라고 알려줍니다. 이어 B는 A에게 968을 제시합니다. A는 자신의 숫자에 8이 들어 있고 위치한 곳이 세 번째 이므로 1Ball 이라고 알려줍니다. 이런 방법으로 번갈아서 숫자를 주고받으면서 예측을 하여 먼저 맞힌 사람이 승리하는 게임입니다.

이 강좌에서는 컴퓨터가 생성한 임의의 세자리 숫자를 알아맞히는 프로그램을 제작해 보도록 하겠습니다.

새 프로젝트를 시작하고 다음과 같이 폼을 디자인 합니다.

Form Design

Form Design

다음의 표를 참고하여 각 컴포넌트의 속성을 적당하게 변경하고 이름을 구분이 가능하도록 지정합니다.

컴포넌트 속성 설명
Button
 Name  : btn1
 Caption  : 1
 숫자1 버튼
Button
 Name  : btn2
 Caption  : 2
 숫자2 버튼
Button
 Name  : btn3
 Caption  : 3
 숫자3 버튼
Button
 Name  : btn4
 Caption  : 4
 숫자4 버튼
Button
 Name  : btn5
 Caption  : 5
 숫자5 버튼
Button
 Name  : btn6
 Caption  : 6
 숫자6 버튼
Button
 Name  : btn7
 Caption  : 7
 숫자7 버튼
Button
 Name  : btn8
 Caption  : 8
 숫자8 버튼
Button
 Name  : btn9
 Caption  : 9
 숫자9 버튼
Button
 Name  : btnOK
 Caption  : 확인
 확인 버튼
Button
 Name  : btnNew
 Caption  : 새게임
 새게임 버튼
Button
 Name  : btnBackSpace
 Caption  : BS
 BS 버튼
Button
 Name  : btnClear
 Caption  : C
 Clear 버튼
Label
 Name  : lblCount
 Caption  : 시도횟수
 제목 라벨
Edit
 Name  : txtNumber
 Text  :
 Readonly  : True
 숫자입력표시 Edit
Edit
 Name  : txtValue
 Text  :
 Visible  : False
 컴퓨터 생성 숫자 저장 Edit
ListBox
 Name  : lsbResult
 숫자 유추 결과 출력

메인폼의 OnActivate 이벤트에 중복이 되지 않은 세자리 숫자를 생성하도록 합니다. 생성한 숫자를 Visible 속성을 False로 설정한 Edit 컴포넌트(txtValue)의 Text 속성에 저장합니다.

procedure TfrmMain.FormActivate(Sender: TObject);
var
tempLoop : Integer;
createdNumber : Array[1..3] of Integer;
begin

txtNumber.Text := ”;
intTryCount := 0;

Randomize;

//세개의 중복되지 않은 숫자를 만들어 숨겨진 TEdit 컴포넌트에 저장
for tempLoop := 1 to 3 do
begin

//루프의 처음이면 바로 숫자 생성
if tempLoop = 1 then
begin
createdNumber[tempLoop] := RandomRange(1,9);
end
//두번째인경우 첫번째 숫자와 같지 않도록 생성된 숫자와 비교하여
//배열에 저장
else if tempLoop = 2 then
begin
createdNumber[tempLoop] := RandomRange(1,9);

while createdNumber[1] = createdNumber[2] do
begin
createdNumber[tempLoop] := RandomRange(1,9);
end

end
//세번째인경우 첫번째, 두번째 숫자와 같지 않도록 생성된 숫자와 비교하여
//배열에 저장
else if tempLoop = 3 then
begin

createdNumber[tempLoop] := RandomRange(1,9);

while (createdNumber[1] = createdNumber[3]) or (createdNumber[2] = createdNumber[3]) do
begin
createdNumber[tempLoop] := RandomRange(1,9);
end

end;

//숨겨진 TEdit 필드에 저장
txtValue.Text := IntToStr(createdNumber[1]) + IntToStr(createdNumber[2]) + IntToStr(createdNumber[3]);
end;

intFirstNumber := StrToInt(Copy(txtValue.Text, 1, 1));
intSecondNumber := StrToInt(Copy(txtValue.Text, 2, 1));
intThirdNumber := StrToInt(Copy(txtValue.Text, 3, 1));

for tempLoop := 1 to 9 do
ChangeButtonStatus(tempLoop, True);

btnOK.Enabled := True;
btnBackSpace.Enabled := True;
btnClear.Enabled := True;
lsbResult.Items.Clear;
end;

 

루프의 처음이면 일단 숫자를 하나 생성하고 배열변수에 저장합니다. 그 다음으로 첫번째 숫자와 같지 않을 때까지 생성하여 배열변수에 저장합니다. 세번째 숫자도 이전과 같은 방법으로 중복되지 않도록 생성합니다. 그리고 나서 각 숫자를 intFirstNumber, intSecondNumber, intThirdNumber에 대입합니다.

그리고 모든 숫자버튼과 결과출력을 초기화 합니다.

맞추려는 숫자가 생성되었으니 사용자로 부터 입력을 받아 그 결과를 ListBox에 출력하면 됩니다.

procedure TfrmMain.btnOKClick(Sender: TObject);
var
intUserFirstNumber, intUserSecondNumber, intUserThirdNumber : Integer;
intStrikeCount, intBallCount : Integer;
intLoop : Integer;
begin

if Length(txtNumber.Text) = 3 then
begin

intStrikeCount := 0;
intBallCount := 0;

if txtNumber.Text = txtValue.Text then //숫자를 맞춘경우
begin
Inc(intTryCount);

lsbResult.Items.Insert(0, IntToStr(intTryCount) + ‘ : ‘ + txtNumber.Text + ‘ Correct.’);
btnOK.Enabled := False;
btnBackSpace.Enabled := False;
btnClear.Enabled := False;
end
else
begin
intUserFirstNumber := StrToInt(Copy(txtNumber.Text, 1, 1));
intUserSecondNumber := StrToInt(Copy(txtNumber.Text, 2, 1));
intUserThirdNumber := StrToInt(Copy(txtNumber.Text, 3, 1));

//첫번째 숫자
if (intUserFirstNumber = intFirstNumber) then
Inc(intStrikeCount)
else
begin
if (intUserFirstNumber = intSecondNumber) then Inc(intBallCount);
if (intUserFirstNumber = intThirdNumber) then Inc(intBallCount);
end;

//두번째 숫자
if (intUserSecondNumber = intSecondNumber) then
Inc(intStrikeCount)
else
begin
if (intUserSecondNumber = intFirstNumber) then Inc(intBallCount);
if (intUserSecondNumber = intThirdNumber) then Inc(intBallCount);
end;

//세번째 숫자
if (intUserThirdNumber = intThirdNumber) then
Inc(intStrikeCount)
else
begin
if (intUserThirdNumber = intFirstNumber) then Inc(intBallCount);
if (intUserThirdNumber = intSecondNumber) then Inc(intBallCount);
end;

Inc(intTryCount);

lsbResult.Items.Insert(0, IntToStr(intTryCount) + ‘ : ‘ + txtNumber.Text + ‘ ‘ + IntToStr(intStrikeCount) + ‘ S ‘ + IntToStr(intBallCount) + ‘ B’);

for intLoop := 1 to 9 do
ChangeButtonStatus(intLoop, True);

txtNumber.Text := ”;

end;

end

end;

 

입력된 숫자를 각각 분리하여 컴퓨터가 생성한 것과 비교하여 그 결과를 출력합니다. ListBox에 Add가 아니라 Insert로 항목을 출력한 것을 마지막에 입력한 숫자가 가장 위에 나타나도록 하기 위함입니다. 판정된 결과를 시도 횟수와 함께 출력해 주어 사용자가 숫자를 추측하기 편하도록 합니다. 이 강좌에서는 이미 입력된 숫자라고 해도 다시 입력 받는데 받지 않도록 개선하면 더 완성도 높은 프로그램이 될 것입니다.

생성된 숫자를 맞추면 더 이상의 입력이 불가능 하도록 확인 버튼을 비활성화 합니다.

나머지 소스는 올려드린 자료를 참고하시면 됩니다.

이 숫자야구 게임은 간단한 프로그램이라 할 수 있는데 별 다른 생각 없이 PC앞에 앉아 바로 코딩을 진행했습니다. 그런데 숫자버튼의 처리나 비교하는 과정에서 바로 생각나는 대로만 하다 보니 나중에는 비슷한 기능을 하는 함수가 여러 개 만들어 지고 기능은 만들어져 있는데 사용되지 않는 함수도 나타나게 되었습니다. 중요한 루틴만을 제외하여 정리하여 필요한 루틴만이 들어가도록 하였습니다. 이것도 완성도가 높다고는 할 수 없습니다. 버튼을 초기화 하는 부분이 중복이 되어 있고 매끄럽지 못한 부분도 눈에 많이 들어옵니다. 한번 스스로 고쳐보시기 바랍니다.

말씀 드리고 싶은 것은 아무리 간단한 프로그램이라고 하더라도 PC앞이 아니라 책상 앞에 앉아 연습장에 생각을 정리하고 어떻게 진행할 것인가를 계획하라는 것 입니다. 물론 약간의 시간이 더 들고 그렇게 할 필요성이 없다라고 생각되시더라도 습관을 들이시는 것이 좋습니다. 저도 간단하게 생각을 하고 바로 코딩을 했다가 다시 한번 뼈저리게 느낀 사실입니다. 한번 더 생각을 정리하면 나중에 많은 시간과 노력을 줄일 수 있음을 꼭 잊지 마시기 바랍니다.

다운로드 : 소스파일

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Time limit is exhausted. Please reload the CAPTCHA.