Chúng ta sẽ khởi đầu code thật tự nhiên với procedure Main
và các comment
về những bước cần thực hiện trong code.
- Ngay khi khởi chạy
app
thì chúng ta sẽ cần phải in ra màn hình tùy chọn để người dùngUser
chọn ký hiệuX
hoặcO
để ghi vào bảngTic-Tac-Toe
và thể hiện các bước đi củaUser
và máy tínhComputer
. - Scan lựa chọn nhập liệu của
User
và lưu lại trạng tháiUser_Symbol
. - Sau khi người dùng đã chọn ký hiệu đại diện thì
app
cần thực hiện khởi tạoState
lưu trữ trạng thái củagameboard
bao gồm các bước đi củaUser
, và các bước củaComputer
, và cả các ô trống còn lại.State
này có thể có chứa thêm các trạng thái khác như nhận địnhMatch_Status
để thể hiện là ván cờ đã kết thúc sau một bước đi nào đó củaUser
hoặcComputer
.
Để khởi đầu đơn giản thì chúng ta sẽ mặc định là User
sẽ luôn luôn được thực hiện bước đi trước. Và trình tự các thủ tục procedure
tiếp theo cần thực hiện là:
- In ra bảng
Tic-Tac-Toe
để người dùngUser
có thể theo dõi trạng thái hiện tại củagameboard
và tính toán bước đi. - Nhận kết quả nhập liệu để mô tả bước đi mà
User
muốn thực hiện. - Cập nhật trạng thái
State
của ứng dụng tương ứng với bước đi vừa rồi củaUser
. - Kiểm tra trạng thái của
gameboard
xem ván cờ đã kết thúc chưa. Nếu kết thúc rồi thìUser
thắng hay là kết quả hòa.
Tương tự, với lượt đi của máy tính Computer
thì chúng ta cũng có các thủ tục như trên:
- In ra bảng
Tic-Tac-Toe
để người dùngUser
có thể theo dõi trạng thái hiện tại củagameboard
và tính toán bước đi. - Gọi trình tự tạo bước đi cho
Computer
dựa trên trạng thái củagameboard
hiện tại. - Cập nhật trạng thái
State
của ứng dụng tương ứng với bước đi vừa rồi củaComputer
. - Kiểm tra trạng thái của
gameboard
xem ván cờ đã kết thúc chưa. Nếu kết thúc rồi thìComputer
thắng hay là kết quả hòa.
Sau một lượt di chuyển của User
và Computer
như thế này thì chúng ta chỉ cần lặp lại các thủ tục từ 3 .. 10
cho đến khi ván cờ kết thúc.
with Ada.Text_IO; use Ada.Text_IO;
with App_Model; use App_Model;
with Console_IO; use Console_IO; procedure Main is -- local
begin -- 0. Put_Symbol_Menu; -- 1. Get_User_Symbol; -- -- 2. Init_App_State; -- loop -- 3. Put_Number_Board; -- 4. Get_User_Move; -- 5. Update_Move_Sets; -- 6. Update_Match_Status; -- -- 7. Put_Number_Board; -- 8. Get_Computer_Move; -- 9. Update_Move_Sets; -- 10. Update_Match_Status; -- -- exit when App_State.Match_Status /= PLAYING; -- end loop;
end Main;
Put_Symbol_Menu;
Đây là kết quả in ra dự kiến của Put_Symbol_Menu;
và Get_User_Symbol;
:
Choose your Symbol ... 1. Letter 'X' 2. Letter 'O'
Your choice:
Chúng ta có thể thiết kế cú pháp sử dụng của các procedure
khởi đầu này ở dạng tương tự với Put (Symbol_Menu);
và Get (User_Symbol);
của thư viện Ada.Text_IO
mà chúng ta đã biết. Tuy nhiên riêng đối với thao tác Put
như vậy thì việc định nghĩa một hằng constant
chứa toàn bộ nội dung của Symbol_Menu
được định dạng thành các dòng và khoảng trống thụt vào như trên sẽ khá rườm rà trong Ada
.
Vì vậy nên ở đây chúng ta sẽ thực hiện thao tác Put_Symbol_Menu
với nội dung của menu
được đặt trực tiếp bên trong procedure
này, và định dạng bằng cách in ra từng dòng. Còn thao tác Get_User_Symbol
sẽ được thiết kế dạng Get (User_Symbol);
Về việc lưu lại kiểu ký hiệu mà User
đã chọn thì tạm thời chúng ta sẽ định nghĩa một kiểu enum Symbol
và lưu vào một biến cục bộ của procedure Main
.
Và trong trường hợp lựa chọn mà người dùng nhập vào không hợp lệ thì chúng ta cần thêm một kiểu exception Error
tự định nghĩa để raise
và xử lý lại thao tác yêu cầu nhập lựa chọn.
package Console_IO is type Symbol is (X, O); SYMBOL_CHOICE_ERROR : exception; procedure Put_Symbol_Menu; procedure Get (User_Symbol : out Symbol);
end Console_IO;
package body Console_IO is procedure Put_Symbol_Menu is ... ; procedure Get (User_Symbol : out Symbol) is ... ; end Console_IO;
Code triển khai của Put_Symbol_Menu;
thì không có gì đáng lưu ý bởi vì chúng ta chỉ đơn giản là Put_Line
từng dòng nội dung trong kết quả mong muốn.
with Ada.Text_IO; use Ada.Text_IO; package body Console_IO is procedure Put_Symbol_Menu is -- local begin Put_Line ("Choose your Symbol ..."); Put_Line (" 1. Letter 'X'"); Put_Line (" 2. Letter 'O'"); end Put_Symbol_Menu; procedure Get (User_Symbol : out Symbol) is ... ; end Console_IO;
Get (User_Symbol);
Thao tác Get (User_Symbol)
cũng khá đơn giản và trình tự cần thực hiện là chúng ta sẽ Put
thông báo Your choice:
để con trỏ không nhảy duống dòng mới và nội dung nhập liệu của User
sẽ xuất hiện trên cùng dòng. Sau đó thực hiện thao tác Get (User_Input)
để hiện con trỏ nhập liệu và nhận vào chuỗi thông tin.
Việc còn lại là chuyển chuỗi nhập liệu thành giá trị số nguyên Chosen_Number
bởi vì nội dung của menu
đang gợi ý người dùng nhập vào 1
hoặc 2
để chọn ký hiệu X
hoặc O
. Sau đó tạo cấu trúc điều kiện để thực hiện gán kết quả phù hợp cho tham số User_Symbol
, hoặc raise SYMBOL_CHOICE_ERROR
nếu người dùng nhập lựa chọn không hợp lệ.
with Ada.Text_IO; use Ada.Text_IO; package body Console_IO is procedure Put_Symbol_Menu is ... procedure Get (User_Symbol : out Symbol) is User_Input : String := "9"; Chosen_Number : Integer; begin Put ("Your choice: "); Get (User_Input); Chosen_Number := Integer'Value (User_Input); case Chosen_Number is when 1 => User_Symbol := X; when 2 => User_Symbol := O; when others => raise SYMBOL_CHOICE_ERROR; end case; Put_Line ("You've chosen: " & Symbol'Image (User_Symbol)); exception when others => Put_Line ("Choice number should be 1 or 2 ..."); Get (User_Symbol); -- end when end Get;
Phần thân của procedure
là khối begin .. exception .. end
ở đây vẫn khá dễ theo dõi do code cần thực hiện cũng không dài lắm. Riêng phần exception
thì ở đây chúng ta có thể sử dụng others
để bao gồm cả những ngoại lệ khác ví dụ như thao tác chuyển kiểu dữ liệu sang từ User_Input
sang Chosen_Number
.
Một lưu ý khác là procedure Get
mà chúng ta tự định nghĩa sẽ được xem là một phiên bản overload
song song với Ada.Text_IO.Get
. Với chữ ký biểu trưng Get (out Symbol)
có thể được trình biên dịch dễ dàng phân biệt với Get (out String)
của Ada.Text_IO
. Vì vậy nên khi chúng ta sử dụng Get (User_Input)
ở đầu procedure
và sau đó lại gọi Get (User_Symbol)
ở phần exception
sẽ không cần tham chiếu từ tên của các package
để phân biệt.
with Ada.Text_IO; use Ada.Text_IO;
with App_Model; use App_Model;
with Console_IO; use Console_IO; procedure Main is User_Symbol : Symbol;
begin Put_Symbol_Menu; Get (User_Symbol); -- -- 2. Init_App_State; -- loop -- 3. Put_Number_Board -- 4. Get_User_Move -- 5. Update_Move_Sets -- 6. Update_Match_Status -- -- 7. Put_Number_Board -- 8. Get_Computer_Move -- 9. Update_Move_Sets -- 10. Update_Match_Status -- -- exit when App_State.Match_Status /= PLAYING; -- end loop;
end Main;
Cuối cùng là code sử dụng tại Main
, chúng ta cần bổ sung biến cục bộ User_Symbol
để lưu ký hiệu mà người dùng đã chọn trong suốt ván cờ.
alr run Choose your Symbol ... 1. Letter 'X' 2. Letter 'O'
Your choice: A
Choice number should be 1 or 2 ...
Your choice: 9
Choice number should be 1 or 2 ...
Your choice: 1
You've chosen: X
[Procedural Programming + Ada] Bài 18 - Console Tic-Tac-Toe App (tiếp theo)