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

Bộ đôi thủ tục tiếp theo là Put_Chess_Board; và Get_User_move; lại rất tương đồng với đoạn xử lý Put_Symbol_Menu; và Get (User_Symbol); trước đó. Put_Chess_Board; Khởi đầu mỗi ván cờ, chúng ta sẽ có một bảng Tic-Tac-Toe với các ô được đánh số 1 .. 9 để người dùng có thể định vị và nhập

Bộ đôi thủ tục tiếp theo là Put_Chess_Board;Get_User_move; lại rất tương đồng với đoạn xử lý Put_Symbol_Menu;Get (User_Symbol); trước đó.

Put_Chess_Board;

Khởi đầu mỗi ván cờ, chúng ta sẽ có một bảng Tic-Tac-Toe với các ô được đánh số 1 .. 9 để người dùng có thể định vị và nhập lựa chọn ô muốn đánh dấu.

    + - - + - - + - - +
    |  2  |  9  |  4  |
    + - - + - - + - - +
    |  7  |  5  |  3  |
    + - - + - - + - - +
    |  6  |  1  |  8  |
    + - - + - - + - - +

Và nếu như sử dụng cách đánh số các ô trên bảng như thế này thì khi kiểm tra để cập nhật Match_Status sau mỗi bước đi của User hoặc Computer, thì chúng ta sẽ kiểm tra xem có bất kỳ bộ 3 số nào trong tập User_Set hoặc Computer_Set có tổng bằng 15 hay không. Nếu có, thì có nghĩa là User hoặc Computer đã hoàn thành được một đường thẳng nào đó trên bảng và thắng ván cờ đó.

Giả sử User_SymbolX thì ký hiệu còn lại O sẽ là của Computer, và chúng ta sẽ tùy vào dữ liệu của User_SetComputer_Set để ghi các ký hiệu tương ứng thay thế vào vị trí của các chữ số trong bảng Tic-Tac-Toe. Như vậy trình in Put_Chess_Board này có phức tạp hơn Put_Symbol_Menu một chút và ở vị trí của mỗi chữ số, chúng ta sẽ cần một function Area_Image để xác định ký ký tự được in ra là ký hiệu X hay O, hay chính chữ số đó.

package Console_IO is
   type Symbol is (X, O);

   SYMBOL_CHOICE_ERROR : exception;
   
   procedure Put_Symbol_Menu;
   procedure Get (User_Symbol : out Symbol);

   procedure Put_Chess_Board
      ( App_State : in State
      ; User_Symbol : in Symbol )
   ; -- return Nothing

   function Area_Image
      ( Area : in Digit
      ; App_State : in State
      ; User_Symbol : in Symbol )
   return String;
end Console_IO;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;

package body Console_IO is

   procedure Put_Symbol_Menu is ...
   
   procedure Get (User_Symbol : out Symbol) is ...
   
   procedure Put_Chess_Board
      ( App_State : in State
      ; User_Symbol : in Symbol ) is ...
   
   function Area_Image
      ( Area : in Digit
      ; App_State : in State
      ; User_Symbol : in Symbol )
   return String is ...

end Console_IO;

Trong Put_Chess_Board thì chúng ta vẫn thực hiện in ra từng dòng copy/paste từ kết quả dự kiến. Và ở vị trí của các chữ số thì chúng ta cần sử dụng coupling function như đã nói ở trên để xác định ký tự sẽ được in ra.

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;

package body Console_IO is

   -- ...

   procedure Put_Chess_Board
      ( App_State : in State
      ; User_Symbol : in Symbol ) is
   begin
      Put_Line ("+ - - + - - + - - +  ");
      Put_Line ("|  " & Area_Image (2, App_State, User_Symbol) & "  |  "
                      & Area_Image (9, App_State, User_Symbol) & "  |  "
                      & Area_Image (4, App_State, User_Symbol) & "  |  ");
      Put_Line ("+ - - + - - + - - +  ");
      Put_Line ("|  " & Area_Image (7, App_State, User_Symbol) & "  |  "
                      & Area_Image (5, App_State, User_Symbol) & "  |  "
                      & Area_Image (3, App_State, User_Symbol) & "  |  ");
      Put_Line ("+ - - + - - + - - +  ");
      Put_Line ("|  " & Area_Image (6, App_State, User_Symbol) & "  |  "
                      & Area_Image (1, App_State, User_Symbol) & "  |  "
                      & Area_Image (8, App_State, User_Symbol) & "  |  ");
      Put_Line ("+ - - + - - + - - +  ");
   end Put_Chess_Board;
   
   -- function Area_Image ...

end Console_IO;

Và ở Area_image, thao tác kiểm tra chữ số được truyền vào đang thuộc *_Set nào sẽ được ủy thác cho một function khác để duy trì logic xử lý ký tự in ra với các nhánh lệnh dễ theo dõi.

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;

package body Console_IO is

   -- ...

   function Area_Image
      ( Area : in Digit
      ; App_State : in State
      ; User_Symbol : in Symbol )
   return String is
      Computer_Symbol : Symbol;
      Result : String := "0";
   begin
      Computer_Symbol := (if User_Symbol = X then O else X);
      --
      if Found (Area, App_State.User_Set) then
         Result := Symbol'Image (User_Symbol);
      elsif Found (Area, App_State.Computer_Set) then
         Result := Symbol'Image (Computer_Symbol);
      else -- Found in App_State.Common_Set
         case Area is
            when 1 => Result := "1";
            when 2 => Result := "2";
            when 3 => Result := "3";
            when 4 => Result := "4";
            when 5 => Result := "5";
            when 6 => Result := "6";
            when 7 => Result := "7";
            when 8 => Result := "8";
            when 9 => Result := "9";
            when others => Result := "0";
         end case;
      end if;
      --
      return Result;
   end Area_Image;

end Console_IO;

Phép kiểm tra xem có tìm thấy Found ô trống Area trong một *_Set nào đó hay không – rất có thể sẽ được sử dụng lại và không hẳn thuộc về mảng làm việc Input/Output với cửa sổ Console; Vì vậy nên chúng ta sẽ định nghĩa tại package App_Model.

package App_Model is

   -- ...

   function Found (Area : Digit; Move_Set : Digit_Array)
   return Boolean;
   
end App_Model;
package body App_Model is

   -- procedure Init ...
   
   function Found
      ( Area : Digit
      ; Move_Set : Digit_Array )
   return Boolean is
      Result : Boolean := FALSE;
   begin
      for Index in Move_Set'Range loop
         if Area = Move_Set (Index) then
            Result := TRUE;
         end if;
      end loop;
      --
      return Result;
   end Found;
   
end App_Model;

Và cuối cùng là code sử dụng ở Main, chúng ta sẽ tạm thời thêm vào một vài câu lệnh để thể hiện một bước đi của User và một bước đi của Computer ở phía trước câu lệnh in bảng để kiểm tra logic hoạt động của Area_ImageFound (Area, Move_Set).

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;
   App_State : State;
begin
   Put_Symbol_Menu;
   Get (User_Symbol);

   Init (App_State);
   Put_Chess_Board (App_State, User_Symbol);
   --
   Put_Line ("User Moved: 1");
   App_State.Common_Set (1) := 0;
   App_State.User_Set (1) := 1;
   --
   Put_Line ("Computer Moved: 9");
   App_State.Common_Set (9) := 0;
   App_State.Computer_Set (9) := 9;
   --
   Put_Chess_Board (App_State, User_Symbol);
   --
   -- 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;
alr run

Choose your Symbol ...
   1. Letter 'X'
   2. Letter 'O'
Your choice: 1
You've chosen: X
+ - - + - - + - - +
|  2  |  9  |  4  |
+ - - + - - + - - +
|  7  |  5  |  3  |
+ - - + - - + - - +
|  6  |  1  |  8  |
+ - - + - - + - - +
User Moved: 1
Computer Moved: 9
+ - - + - - + - - +
|  2  |  O  |  4  |
+ - - + - - + - - +
|  7  |  5  |  3  |
+ - - + - - + - - +
|  6  |  X  |  8  |
+ - - + - - + - - +

Get_User_Move;

Thao tác Get_User_Move; cũng có thể thiết kế với cú pháp sử dụng giống như Get (User_Symbol); trước đó. và kết quả sẽ được lưu lại vào biến cục bộ User_move của procedure Main. Trong trường hợp giá trị người dùng nhập vào ngoài khoảng 1 .. 9 thì chúng ta sẽ cần phải phát động ngoại lệ để đưa ra thông báo và yêu cầu thực hiện lại việc nhập liệu.

with App_Model; use App_Model;

package Console_IO is
   type Symbol is (X, O);

   SYMBOL_CHOICE_ERROR : exception;
   MOVE_CHOICE_ERROR : exception;
   
   procedure Put_Symbol_Menu;
   procedure Get (User_Symbol : out Symbol);

   procedure Put_Chess_Board ...
   procedure Get (User_Move : out Digit; App_State : State);

   function Area_Image ...
   return String;
end Console_IO;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;

package body Console_IO is

   -- ...

   procedure Get (User_Move : out Digit; App_State : in State) is
      User_Input : String := "0";
      Chosen_Number : Integer;
   begin
      Put ("Your move: "); Get (User_Input);
      Chosen_Number := Integer'Value (User_Input);
      --
      if Chosen_Number = 0 then
         raise MOVE_CHOICE_ERROR with "Choice number should be in 1 .. 9";
      elsif not Found (Chosen_Number, App_State.Common_Set) then
         raise MOVE_CHOICE_ERROR with "The number has been taken before.";
      else
         User_Move := Chosen_Number;
      end if;
   exception
      when Exc : MOVE_CHOICE_ERROR =>
         Put_Line (Exception_Message (Exc));
         Get (User_Move, App_State);
      when others =>
         Put_Line ("Choice number should be in 1 .. 9 and has not been taken.");
         Get (User_Move, App_State);
   end Get;
   
   -- function Area_Image ...
   
end Console_IO;

Và ở code sử dụng tại Main chúng ta sẽ tạm thời thêm lệnh Put_Line để kiểm tra kết quả nhập liệu.

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;
   App_State : State;
   User_Move : Digit;
begin
   Put_Symbol_Menu;
   Get (User_Symbol);

   Init (App_State);
   Put_Chess_Board (App_State, User_Symbol);
   
   -- loop
      Get (User_Move, App_State); Put_Line ("User Moved: " & Digit'Image (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;
Choose your Symbol ...
   1. Letter 'X'
   2. Letter 'O'
Your choice: 1
You've chosen: X
+ - - + - - + - - +
|  2  |  9  |  4  |
+ - - + - - + - - +
|  7  |  5  |  3  |
+ - - + - - + - - +
|  6  |  1  |  8  |
+ - - + - - + - - +
Your move: 0
Choice number should be in 1 .. 9
Your move: A
Choice number should be in 1 .. 9
Your move: 1
User Moved:  1

[Procedural Programming + Ada] Bài 17 – 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 đầ