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

Bây giờ chúng ta sẽ bổ sung thêm logic xử lý bước đi cuối cùng cho Computer;Và với thời lượng trung bình của các bài viết mà mình đang áp dụng thì rất có thể chúng ta sẽ cần thêm vài bài viết nữa để hoàn thành procedure này kèm theo cả một phần nội

Bây giờ chúng ta sẽ bổ sung thêm logic xử lý bước đi cuối cùng cho Computer;Và với thời lượng trung bình của các bài viết mà mình đang áp dụng thì rất có thể chúng ta sẽ cần thêm vài bài viết nữa để hoàn thành procedure này kèm theo cả một phần nội dung tóm lược về những thứ đáng lưu ý nhất mà mình nghiệm thu được về Procedural Programming nhờ sự giúp đỡ của Ada.

Ở đây chúng ta sẽ nói một chút về cách tư duy xử lý trường hợp có nhiều Double_Threat ở cuối bài viết trước.

User : X
Computer : O

+ - - + - - + - - +
|     |     |  X  |
+ - - + - - + - - +
|     |  O  |     |
+ - - + - - + - - +
|  X  |     |     |
+ - - + - - + - - +
Computer move : 

Giả sử nếu đặt mình vào vị trí xử lý lượt đi của Computer thì chúng ta sẽ thấy rằng ở lượt tiếp theo User phía bên kia đang có cơ hội tạo Double_Threat nếu chọn ô ở góc phía trên bên trái hoặc góc phía dưới bên phải. Rõ ràng là việc chặn trực tiếp Block_Double_Threat sẽ không giải quyết được tình huống để cân bằng kết quả của ván cờ. Nếu vậy chúng ta cần tạo ra một cơ hội thắng trực tiếp Direct_Chance để buộc phía User phải ưu tiên việc chặn lại thay vì tạo Double_Threat ở bước đi tiếp theo như họ dự tính.

Chúng ta có thể sẽ có nhiều lựa chọn ở bước đi tiếp theo để khởi tạo cơ hội thắng trực tiếp với bất kỳ ô trống nào có khả năng kết hợp với các ô đã đánh dấu O để tạo ra đường thẳng gần hoàn thiện. Tuy nhiên, mục tiêu của chúng ta trong trường hợp này không chỉ đơn thuần là tạo ra Direct_Chance mà là khiến cho User phải chọn vào Direct_Chance để chặn và chệch hướng khỏi các bước đi tạo Double_Threat. Và điều đó có nghĩa là khi chọn ra một giá trị Direct_Chance cho Computer thì chúng ta phải loại trừ trường hợp giá trị này cũng đồng thời có mặt trong tập Double_Threat từ phía User.

Vậy chúng ta sẽ gọi bước đi tiên đoán để khởi tạo cơ hội là Direct_Chance_Init, và bước đi thắng cuộc mà User cần phải chặn là Direct_Chance. Và những procedure hỗ trợ mà chúng ta cần có là:

  • Get_Double_Threats; – Tiên đoán tập giá trị các bước đi mà User có thể đang hướng tới để tạo Double_Threat.
  • Get_Direct_Chance_Inits; – Tiên đoán tập giá trị các bước đi sẽ khởi tạo cơ hội thắng cuộc trực tiếp cho Computer.
  • Get_Direct_Chance; – Tiên đoán bước đi sẽ hoàn thành đường thẳng thắng cuộc của Computer tương ứng với bước đi đã tiên đoán trước đó và đồng thời đây cũng chính là bước đi mà Computer cần phải chọn vào để chặn lại.

Và tệp khai báo cấu hình của package AI_Mockup sẽ có danh sách đầy đủ với các sub-program mới đang tạm thời được -- comment như sau:

with App_Model; use App_Model;

package AI_Mockup is
   procedure Get (Computer_Move : out Digit; App_State : in State);

   procedure Get_Prioritized (Computer_Move : out Digit; App_State : in State);

   procedure Block_Direct_Winning (Computer_Move : in out Digit; App_State : in State);

   procedure Block_Double_Threat (Computer_Move : in out Digit; App_State : in State);

   -- procedure Redirect_User_Concern (Computer_Move : in out Digit; App_State : in State);

   -- procedure Get_Double_Threats (User_Moves : in out Digit_Array; App_State : in State);

   -- procedure Get_Direct_Chance_Inits (Computer_Moves : in out Digit_Array; App_State : in State);

   -- procedure Get_Direct_Chance (Computer_Move : in out Digit; App_State : in State);

   function Count_Direct_Threats (App_State : State)
   return Integer;

   -- function Count_Double_Threats (App_State : State)
   -- return Integer;

   -- function Count_Direct_Chances (App_State : State)
   -- return Integer;
end AI_Mockup;

Chúng ta sẽ bắt đầu với dòng code bổ sung logic cho procedure Get (Computer_Move) và bỏ dần -- comment để viết code cho từng procedure hỗ trợ đang dự kiến. Logic xử lý mới bổ sung có tên gọi là Redirect_User_Concern; và có mức ưu tiên cao hơn so với Block_Double_Threat và thấp hơn so với Block_Direct_Winning.

with Ada.Text_IO; use Ada.Text_IO;

package body AI_Mockup is

   procedure Get
      ( Computer_Move : out Digit
      ; App_State : in State ) is
   begin
      Get_Prioritized (Computer_Move, App_State);
      Block_Double_Threat (Computer_Move, App_State);
      Redirect_User_Concern (Computer_Move, App_State); -- new
      Block_Direct_Winning (Computer_Move, App_State);
      --
      Put_Line ("Computer move:" & Digit'Image (Computer_Move));
   end Get;
   
   -- ...
   
end AI_Mockup;

Và ở đây chúng ta đang dự kiến rằng Redirect_User_Concern; sẽ chỉ thực sự hoạt động khi nhìn thấy nhiều Double_Threat, bởi nếu chỉ thấy một Double_Threat duy nhất thì chúng ta đã có kết quả mong muốn ở dòng gọi Block_Double_Threat; ngay trước đó rồi. Như vậy chúng ta sẽ cần tạo ra thao tác hỗ trợ để đếm số lượng Count_Double_Threats trước khi quyết định chạy logic xử lý tình huống.

with Ada.Text_IO; use Ada.Text_IO;

package body AI_Mockup is

   -- ...
   
   procedure Redirect_User_Concern
      ( Computer_Move : in out Digit
      ; App_State : in State ) is
   begin
      if Count_Double_Threats (App_State) > 1 then
         Put_Line ("Double_Threats : " & Integer'Image (Count_Double_Threats (App_State)));
      end if;
   end Redirect_User_Concern;
   
   -- ...

   -- function Count_Direct_Threats ...
   
   function Count_Double_Threats (App_State : State)
      return Integer
   is -- local
      Foreseen_User_Move : Digit := 0;
      Foreseen_App_State : State;
      Counter : Integer := 0;
   begin
      for Index in App_State.Common_Set'Range loop
         if App_State.Common_Set (Index) /= 0 then
            Copy (Foreseen_App_State, App_State);
            Foreseen_User_Move := App_State.Common_Set (Index);
            Update_User_Set (Foreseen_App_State, Foreseen_User_Move);
            --
            if Count_Direct_Threats (Foreseen_App_State) > 1 then
               Counter := Counter + 1;
            end if;
         end if;
      end loop;
      --
      return Counter;
   end Count_Double_Threats;

end AI_Mockup;

Ở đây chúng ta xem như bước đi tiếp theo của User có thể là bất kỳ ô trống nào đang còn lại trên bàn cờ hiện tại bởi vì chúng ta chưa chọn cố định bước đi cho Computer. Và như vậy sẽ cần lặp qua mỗi giá trị có ý nghĩa của Common_Set với giả định là sẽ được User chọn để tạo ra các phiên bản tiên đoán Forseen_App_State. Sau đó với trường hợp nào mà chúng ta tìm thấy số lượng nguy cơ trực tiếp Count_Direct_Threats là số nhiều thì chúng ta tăng biến đếm số lượng lên một đơn vị.

Bây giờ chúng ta cần kiểm tra lại trường hợp ván cờ ở đầu bài viết, User đang có các khả năng tạo ra Double_Threat ở các ô trống góc phía trên bên trái và góc phía dưới bên phải.

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: 6

PLAYING ...
+ - - + - - + - - +
|  2  |  9  |  4  |
+ - - + - - + - - +
|  7  |  5  |  3  |
+ - - + - - + - - +
|  X  |  1  |  8  |
+ - - + - - + - - +
Computer move: 5

PLAYING ...
+ - - + - - + - - +
|  2  |  9  |  4  |
+ - - + - - + - - +
|  7  |  O  |  3  |
+ - - + - - + - - +
|  X  |  1  |  8  |
+ - - + - - + - - +
Your move: 4

PLAYING ...
+ - - + - - + - - +        + - - + - - + - - +
|  2  |  9  |  X  |   =>   |  ?  |     |  X  |
+ - - + - - + - - +        + - - + - - + - - +
|  7  |  O  |  3  |   =>   |     |  O  |     |
+ - - + - - + - - +        + - - + - - + - - +
|  X  |  1  |  8  |   =>   |  X  |     |  ?  |
+ - - + - - + - - +        + - - + - - + - - +
Double_Threats :  2
Computer move: _

Và một tình huống khác khi User đi trước ở ô trống trung tâm với nhiều khả năng tạo Double_Threat hơn.

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: 5

PLAYING ...
+ - - + - - + - - +
|  2  |  9  |  4  |
+ - - + - - + - - +
|  7  |  X  |  3  |
+ - - + - - + - - +
|  6  |  1  |  8  |
+ - - + - - + - - +
Computer move: 2

PLAYING ...
+ - - + - - + - - +
|  O  |  9  |  4  |
+ - - + - - + - - +
|  7  |  X  |  3  |
+ - - + - - + - - +
|  6  |  1  |  8  |
+ - - + - - + - - +
Your move: 8

PLAYING ...
+ - - + - - + - - +        + - - + - - + - - +
|  O  |  9  |  4  |   =>   |  O  |     |  ?  |
+ - - + - - + - - +        + - - + - - + - - +
|  7  |  X  |  3  |   =>   |     |  X  |  ?  |
+ - - + - - + - - +        + - - + - - + - - +
|  6  |  1  |  X  |   =>   |  ?  |  ?  |  X  |
+ - - + - - + - - +        + - - + - - + - - +
Double_Threats :  4
Computer move: _

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