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

Thao tác cập nhật User_Set sau khi người dùng nhập vào bước đi muốn thực hiện trên bàn cờ chỉ đơn giản là chúng ta sẽ di chuyển giá trị User_Move từ mảng Common_Set tới vị trí tương ứng trong mảng User_Set. Tuy nhiên chúng ta có lẽ sẽ muốn tạo ra một cơ chế

Thao tác cập nhật User_Set sau khi người dùng nhập vào bước đi muốn thực hiện trên bàn cờ chỉ đơn giản là chúng ta sẽ di chuyển giá trị User_Move từ mảng Common_Set tới vị trí tương ứng trong mảng User_Set. Tuy nhiên chúng ta có lẽ sẽ muốn tạo ra một cơ chế để đảm bảo rằng sau bất kỳ thủ tục cập nhật nào thì các *_Set vẫn sẽ bổ trợ lẫn nhau, và khi gộp các *_Set lại thì chúng ta sẽ thu được chính xác vừa đủ một bộ số 1 .. 9 như mảng Common_Set ban đầu:

  • Areas_Are_Unique (App_State); – đảm bảo rằng mỗi giá trị có ý nghĩa (khác 0) là duy nhất và không bị lặp lại trong phạm vi bao gồm tất cả các *_Set.
  • Sets_Are_Complement (App_State); – đảm bảo rằng tập các giá trị có ý nghĩa trong các *_Set hoàn thiện lẫn nhau và thể hiện vừa đủ bộ số nguyên 1 .. 9.

Như vậy là trong package App_Model, ngoài các type tự định nghĩa thì chúng ta sẽ có thêm các procedureInitUpdate_User_Set, và các function hỗ trợ sau:

package App_Model is
   -- type ...

   procedure Init (App_State : out State)
   with Post => Areas_Are_Unique (App_State) and Sets_Are_Complement (App_State);

   procedure Update_User_Set (App_State : out State; User_Move : in Digit)
   with Post => Areas_Are_Unique (App_State) and Sets_Are_Complement (App_State);

   function Areas_Are_Unique (App_State : State)
   return Boolean;

   function Sets_Are_Complement (App_State : State)
   return Boolean;
   
   function Found (Area : Digit; Move_Set : Digit_Array)
   return Boolean;
end App_Model;

Found (Area, Move_Set); đã được định nghĩa trong bài trước để hỗ trợ cho thao tác kiểm tra bước đi User muốn thực hiện vẫn đang có mặt trong Common_Set. Các procedure sử dụng tham số out State có khả năng sẽ chỉnh sửa các *_Set và chúng ta có thể khai báo gắn kèm contract Post để đảm bảo rằng trong tổng bộ các *_Set không có giá trị có nghĩa nào bị lặp hay bị mất đi so với tập 1 .. 9 ban đầu.

Areas_Are_Unique;

Để đảm bảo các giá trị thể hiện các ô area là duy nhất và không bị lặp lại trong tổng bộ các *_Set thì chúng ta có thể kiểm tra để loại trừ khả năng một giá trị xuất hiện trong nhiều hơn một *_Set; Hoặc thực hiện nối nội dung các mảng concat sau đó kiểm tra tần suất xuất hiện của mỗi phần từ của tập 1 .. 9 trong mảng Concatenated.

package body App_Model is
   -- ...
   
   function Areas_Are_Unique (App_State : State)
      return Boolean
   is -- local
      Origin_Set : Digit_Array;
      Area : Digit;
      Found_Duplicated : Boolean;
      Result : Boolean := TRUE;
   begin
      Origin_Set := (1, 2, 3, 4, 5, 6, 7, 8, 9);
      --
      for Index in Origin_Set'Range loop
         Area := Origin_Set (Index);
         Found_Duplicated := (Found (Area, App_State.Common_Set) and Found (Area, App_State.User_Set))
                          or (Found (Area, App_State.Common_Set) and Found (Area, App_State.Computer_Set))
                          or (Found (Area, App_State.User_Set)   and Found (Area, App_State.Computer_Set));
         if Found_Duplicated then
            Result := FALSE;
         end if;
      end loop;
      --
      return Result;
   end Areas_Are_Unique;
   
   -- ...
end App_Model;

Sets_Are_Complement;

Để kiểm tra các giá trị có nghĩa trong tổng bộ các *_Set vẫn hoàn thiện đủ tập giá trị 1 .. 9 thì chúng ta có thể thực hiện sát nhập mảng merge. Thao tác gộp sẽ là một dạng ghi đè đối với các trường hợp có giá trị trùng lặp (nếu có).

package body App_Model is
   -- ...

   function Sets_Are_Complement (App_State : State)
      return Boolean
   is -- local
      Merged_Set : Digit_Array := (others => 0);
      Area : Digit := 0;
      Result : Boolean := TRUE;
   begin
      --
      for Index in Merged_Set'Range loop
         Area := App_State.Common_Set (Index);
         if Area /= 0 then
            Merged_Set (Index) := Area;
         end if;
         --
         Area := App_State.User_Set (Index);
         if Area /= 0 then
            Merged_Set (Index) := Area;
         end if;
         --
         Area := App_State.Computer_Set (Index);
         if Area /= 0 then
            Merged_Set (Index) := Area;
         end if;
      end loop;
      --
      for Index in Merged_Set'Range loop
         if Merged_Set (Index) = 0 then
            Result := FALSE;
         end if;
      end loop;
      --
      return Result;
   end Sets_Are_Complement;
end App_Model;

   -- ...
end App_Model;

Update_User_Set;

Và với các contract Post đã có thì nội dung của Update_User_Set sẽ không có gì phải xử lý thêm ngoài việc chuyển giá trị tương tứng với User_Move trong Common_Set sang User_Set.

package body App_Model is
   -- ...

   procedure Update_User_Set
      ( App_State : out State
      ; User_Move : in Digit )
   is -- local
      Index : Integer := User_Move;
   begin
      App_State.Common_Set (Index) := 0;
      App_State.User_Set (Index) := User_Move;
   end Update_User_Set;
   
   -- ...
end App_Model;

Cuối cùng là code sử dụng tại Main, chúng ta sẽ đặt thêm Put_Chess_Board; ở lượt của Computer để hiển thị kết quả hoạt động của Update_User_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;
   User_Move : Digit;
begin
   Put_Symbol_Menu;
   Get (User_Symbol);

   Init (App_State);
   -- loop
      Put_Chess_Board (App_State, User_Symbol);
      Get (User_Move, App_State);

      Update_User_Set (App_State, User_Move);
      -- 6. Update_Match_Status
      --
      Put_Chess_Board (App_State, User_Symbol);
      -- 8. Get_Computer_Move
      -- 9. Update_Computer_Set
      -- 10. Update_Match_Status
      --
      -- 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  |
+ - - + - - + - - +
Your move: 0
Choice number should be in 1 .. 9
Your move: A
Choice number should be in 1 .. 9 and has not been taken.
Your move: 1
+ - - + - - + - - +  
|  2  |  9  |  4  |
+ - - + - - + - - +
|  7  |  5  |  3  |
+ - - + - - + - - +
|  6  |  X  |  8  |
+ - - + - - + - - +

(chưa đăng tải) [Procedural Programming + Ada] Bài 21 – 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 đầ