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ạoDouble_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 choComputer
.Get_Direct_Chance;
- Tiên đoán bước đi sẽ hoàn thành đường thẳng thắng cuộc củaComputer
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)