[Procedural Programming + Ada] Bài 14 – Console Tic-Tac-Toe App (tiếp theo)

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ùng User chọn ký hiệu X hoặc O để ghi vào bảng Tic-Tac-Toe

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.

  1. 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ùng User chọn ký hiệu X hoặc O để ghi vào bảng Tic-Tac-Toe và thể hiện các bước đi của User và máy tính Computer.
  2. Scan lựa chọn nhập liệu của User và lưu lại trạng thái User_Symbol.
  3. 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ạo State lưu trữ trạng thái của gameboard bao gồm các bước đi của User, và các bước của Computer, 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 định Match_Status để thể hiện là ván cờ đã kết thúc sau một bước đi nào đó của User hoặc Computer.

Để 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à:

  1. In ra bảng Tic-Tac-Toe để người dùng User có thể theo dõi trạng thái hiện tại của gameboard và tính toán bước đi.

  2. Nhận kết quả nhập liệu để mô tả bước đi mà User muốn thực hiện.

  3. 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ủa User.

  4. 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.

  5. In ra bảng Tic-Tac-Toe để người dùng User có thể theo dõi trạng thái hiện tại của gameboard và tính toán bước đi.

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:

  1. Gọi trình tự tạo bước đi cho Computer dựa trên trạng thái của gameboard hiện tại.
  2. 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ủa Computer.
  3. 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.
  4. In ra bảng Tic-Tac-Toe để người dùng User có thể theo dõi trạng thái hiện tại của gameboard và tính toán bước đi.

Sau một lượt di chuyển của UserComputer như thế này thì chúng ta chỉ cần lặp lại các thủ tục từ 5 trở đi 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
   -- 1. Put_Symbol_Menu;
   -- 2. Get_User_Symbol;
   --
   -- 3. Init_App_State;
   -- 4. Put_Chess_Board;
   --
   -- loop
      -- 5. Get_User_Move;
      -- 6. Update_User_Set;
      -- 7. Update_Match_Status;
      -- 8. Put_Chess_Board;
      --
      -- 9. Get_Computer_Move;
      -- 10. Update_Computer_Set;
      -- 11. Update_Match_Status;
      -- 12. Put_Chess_Board;
      --
      -- 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;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);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 := 9;
   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);
   --
   -- 3. Init_App_State;
   -- 4. Put_Chess_Board;
   --
   -- loop
      --
      -- 5. Get_User_Move;
      -- 6. Update_User_Set;
      -- 7. Update_Match_Status;
      -- 8. Put_Chess_Board;
      --
      -- 9. Get_Computer_Move
      -- 10. Update_Computer_Set
      -- 11. Update_Match_Status
      -- 12. Put_Chess_Board;
      --
      -- 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 15 – Console Tic-Tac-Toe App (tiếp theo)

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ