Skip to main content

Ứng dụng giải thuật MiniMax trong trò chơi cờ tướng - Tìm hiểu về trí tuệ nhân tạo (Phần 2)

Chắc hẳn mọi người đều biết về trò chơi thú vị như cờ tướng. Tiếp theo loạt bài về trí tuệ nhân tạo, bài viết này mình sẽ nói về cụ thể giải thuật Minimax ứng dụng trong trò chơi trí tuệ cờ tướng như thế nào. OK! Let's go.

1. Ý tưởng
Cờ tướng là trò chơi đối kháng, trong đó hai người luôn phiên nhau đi nước đi của mình. Trạng thái bắt đầu là trạng thái khởi tạo bàn cờ, sau mỗi nước đi của một bên, trạng thái bàn cờ sẽ được thay đổi thành một trạng thái mới hiện hành. Cờ tướng có luật của nó, và trò chơi sẽ kết thúc khi một người có được trạng thái phản ánh sự thắng cuộc hoặc hai người rơi vào trạng thái hòa cờ. Ta tìm cách phân tích xem từ một trạng thái nào đó sẽ dẫn đến đấu thủ nào sẽ thắng với điều kiện cả hai có trình độ như nhau. Giải thuật Minimax sẽ được áp dụng vào trong trò chơi cờ tướng. Hai đấu thủ trong trò chơi sẽ được gọi là MIN và MAX và hai đấu thủ đều biết rõ thông tin trên bàn cờ như nhau. 
- MAX đại diện cho đấu thủ quyết dành thắng lợi hoặc tối đa hóa ưu thế của mình.
 - MIN ngược lại, cố gắng tối thiểu hóa điểm số của MAX.

Bàn cờ tướng cũng chính là một không gian trạng thái với các mức và được biểu diễn bằng cây trò chơi. Mỗi nút của cây biểu diễn cho một trạng thái trên bàn cờ. Nút gốc biểu diễn trạng thái bắt đầu ván cờ. Các nút lá thể hiện trạng thái kết thúc của trò chơi (khi một đấu thủ giành phần thắng, thua, hay hai đấu thủ hòa nhau).
Hai đấu thủ được gọi là MAX và MIN và luân phiên đi nước cờ của mình nên mỗi mức trên cây được biểu diễn luân phiên là MAX và MIN. Các nút ứng với trạng thái mà từ đó đấu thủ MAX chọn nước đi ứng với lớp MAX, các nút mà đấu thủ MIN chọn nước đi ứng với lớp MIN. Với mỗi nước đi trong bàn cờ, tương ứng với các mức trên cây, giải thuật minimax sẽ định giá trị cho các nút như sau:
-         Nếu nút là nút lá gán cho nút đó một giá trị để phản ánh trạng thái thắng, thua hay hòa của các đấu thủ
-         Sử dụng giá trị các nút lá để xác định giá trị của các nút ở mức trên nó:
+ Nút thuộc lớp MAX gán cho nó giá trị lớn nhất trong các nút lá.
+ Nút thuộc lớp MIN gán cho nó giá trị nhỏ nhất trong các nút lá.
Gán giá trị cho từng thế cờ theo quy tắc trên chỉ rõ giá trị của trạng thái tốt nhất mà mỗi đấu thủ hi vọng đạt được. Cũng giống như việc tính toán nước đi và thực hiện một nước cờ thực sự của đấu thủ. Giải thuật Minimax thể hiện việc tính toán nước đi tối ưu cho các đấu thủ thông quá giá trị của các nút được gán. Trong một nước cờ, khi đấu thủ MAX đến lượt đi, đấu thủ này sẽ chọn nước đi ứng với trạng thái có giá trị cao nhất trong các trạng thái con, còn đấu thủ MIN sẽ chọn một nước đi ứng với trạng thái có giá trị nhỏ nhất trong các trạng thái con.
Tuy nhiên, cờ tướng là một trò chơi có thể nói là phức tạp, việc mở rộng không gian trạng thái khí áp dụng giải thuật Minimax có thể gặp khó khăn. Vì thế mà chúng ta chỉ xét đến việc triển khai giải thuật Minimax với  trò chơi cờ tướng có mức độ sâu được định trước (khoảng 5 mức). Việc định trước mức hay độ sâu sẽ làm giảm thời gian tính toán cho giải thuật và AI (máy) sẽ đưa ra được một nước đi nhanh hơn và chính xác hơn. Phần sau đây mình sẽ trình bày về quá trình xây dựng giải thuật Minimax.

2. Xây dựng giải thuật
Giả sử chúng ta có một bộ phân tích thế cờ có thể áp dụng tất cả các luật, các phương pháp đánh cờ khác nhau vào từng thế cờ và chuyển đổi chúng thành một con số đại diện (cho điểm thế cờ). Mặt khác, ta giả sử con số đó là dương khi áp dụng cho thế cờ của một đấu thủ (được gọi là đấu thủ MAX), và là âm khi áp dụng cho đấu thủ bên kia (được gọi là đấu thủ MIN). Quá trình tính toán cho điểm thế cờ được gọi là lượng giá tĩnh (static evaluation). Hàm thực hiện việc tính toán được gọi là một bộ lượng giá tĩnh và giá trị nhận được gọi là điểm lượng giá tĩnh. Cả hai đấu thủ đều cố gắng đi như thế nào đó để đạt được điểm tuyệt đối lớn nhất. Đấu thủ MAX sẽ tìm những nước đi dẫn đến điểm của mình trở nên lớn hơn (hay cao nhất có thể được) hay điểm của đối thủ bớt âm hơn (nhỏ hơn về giá trị tuyệt đối). Còn đấu thủ MIN lại ra sức phản kháng lại, để dẫn tới điểm âm của anh ta âm hơn hay điểm dương của đối thủ nhỏ đi.

Hình 2.4 Minh họa chiến lược chơi cờ của người lẫn máy

Ta có một ví dụ:


Hình 2.5 Cây trò chơi thể hiện giải thuật Minimax
Trong ví dụ này: Người chơi cực đại hi vọng chọn nước đi bên phải để đạt được điểm 8. Thế nhưng nếu đi như vậy thì khi đến lượt đi của người chơi cực tiểu, anh ta sẽ cố gắng không cho người chơi cực đại đạt được điểm này bằng cách chọn nước đi nhánh bên trái và như vậy, người chơi cực đại chỉ được có 1 điểm thay vì 8. Ngược lại, nếu người chơi cực đại chọn nước đi bên trái, thì trong tình huống xấu nhất anh ta vẫn còn được 2 điểm, lớn hơn là chọn nước đi bên phải. Nói chung, người chơi cực đại sẽ phải tìm cách nhận ra các nước đi của đối phương tiếp theo làm cho điểm giảm xuống. Và tương tự như vậy, người chơi cực tiểu phải nhận biết được nước đi của người chơi cực đại cố gắng làm tăng điểm lên. Thủ tục tìm nước đi tốt nhất trên cây trò chơi như trên được gọi là thủ tục Minimax, do điểm ở mỗi nút có thể là điểm cực đại hoặc có thể là điểm cực tiểu và có tư tưởng thuật toán như sau:

1. Nếu như đạt đến giới hạn tìm kiếm (đến tầng dưới cùng của cây tìm kiếm), tính giá trị tĩnh của thế cờ hiện tại ứng với người chơi ở đó. Ghi nhớ kết quả.
2. Nếu như mức đang xét là của người chơi cực tiểu, áp dụng thủ tục Minimax này cho các con của nó. Ghi nhớ kết quả nhỏ nhất.
3. Nếu như mức đang xét là của người chơi cực đại, áp dụng thủ tục Minimax này cho các con của nó. Ghi nhớ kết quả lớn nhất

Từ ý tưởng phân tích trên ta có thể xây dựng thủ tục Minimax như sau:
Hàm Minimax nhận vào một thế cờ pos và trả về giá trị của thế cờ đó. Nếu thế cờ pos tương ứng với nút lá trong cây trò chơi thì trả về giá trị đã được gán cho nút lá. Ngược lại ta cho pos một giá trị tạm value là - vô cùng hoặc + vô cùng tùy thuộc pos là nút MAX hay MIN và xét các thế cờ con của pos. Sau khi một con của pos có giá trị V thì đặt lại value = max(value, V) nếu n là nút MAX và value = min( value, V) nếu n là nút MIN. Khi tất cả các con của n đã được xét thì giá trị tạm value của pos trở thành giá trị của nó. (INFINITY thể hiện cho giá trị vô cùng).
Ta có giả mã cho giải thuật Minimax như sau:
function Minimax(pos): integer;
value, best: integer;
begin
    if pos là nút lá then return eval(pos)
    else
    begin
          {Khởi tạo giá trị tạm cho best}
           if pos là nút MAX then
                     best:= -INFINITY
               else best:= INFINITY;
                        {hàm gen() sinh ra mọi nước đi từ thế cờ pos}
             gen(pos);
               {Xét tất cả các con của pos, mỗi lần xác định được giá trị của một nút con, ta phải đặt lại giá trị tạm value. Khi đã xét hết tất cả các con thì value là giá trị của n}
            while (còn lấy được một nước đi m) do
            begin
                        pos:= Tính thế cờ mới nhờ đi m;
                        value = Minimax (pos);
                        if   pos là nút MAX then
                             if  (value > best) then best := value;
                        if  pos là nút MIN then
                             if (value < best) then best:= value;
            end;
            Minimax :=best;
         end;
       end;
Xem xét đoạn chương trình trên ta thấy:
Có hai hàm mới là hàm eval(pos) và hàm gen(pos). Hàm eval(pos) thực hiện việc tính giá trị (lượng giá) của thế cờ pos. Hàm gen(pos) sinh ra tất cả các nước đi có thể từ thế cờ pos hiện tại. Việc xây dựng hai hàm này sẽ phụ thuộc vào từng trò chơi cụ thể.
Hàm đánh giá Eval ứng với mỗi trạng thái (thế cờ) pos của trò chơi với một giá trị số Eval(pos). Giá trị này là sự đánh giá độ lợi thế của trạng thái pos. Trạng thái pos càng thuận lợi cho MAX thì Eval(pos) là số dương càng lớn, pos càng thuận lợi cho MIN thì eval(pos) là số âm càng nhỏ. Eval(pos) xấp xỉ 0 đối với trạng thái không lợi thế cho ai cả. Chất lượng của chương trình chơi cờ phụ thuộc rất nhiều vào hàm đánh giá. Nếu hàm đánh giá cho ta sự đánh giá không chính xác về các trạng thái, nó có thể hướng ta đi tới trạng thái được xem là tốt, nhưng thực tế lại bất lợi cho ta. Thiết kế một hàm đánh giá tốt là một việc khó. Đòi hỏi ta phải quan tâm đến nhiều nhân tố. Ở đây có sự mâu thuẫn giữa độ chính xác của hàm đánh giá và thời gian tính của nó. Hàm đánh giá chính xác sẽ đòi hỏi rất nhiều thời gian tính toán, mà người chơi lại bị giới hạn bởi thời gian đưa ra nước đi.
Như chúng ta đã biết, không phải lúc nào cũng đến được tận các nút lá. Đặc biệt trong các trò chơi cờ, bị giới hạn thời gian suy nghĩ của mỗi đối thủ đồng thời số nút và độ sâu của cây trò chơi là rất lớn. Chúng ta khó có thể thực hiện tìm kiếm đến độ sâu của các nút là. Vì vậy, chúng ta thực hiện tìm đến một độ sâu depth cố định nào đó. Độ sâu depth càng lớn, hàm tìm kiếm càng gần giá trị tối ưu, cũng có nghĩa là "trình độ suy nghĩ" của máy càng cao! Như vậy, hàm Minimax bây giờ sẽ có lời gọi: Minimax(pos, depth) trong đó depth là độ sâu tìm kiếm.
Thông thường, để cho tiện (và cùng rất gần thực tế) ta coi cả hai người chơi có cùng cách đánh giá về một thế cờ. Có điều thế cờ này là tốt với một người thì phải được đánh giá là tồi với người kia và ngược lại. Trong máy tính cách thể hiện tốt nhất là ta cho điểm một thế cờ có thêm dấu âm dương: dấu dương dành cho người chơi cực đại và dấu âm cho người chơi cực tiểu. Với người chơi cực đại sẽ mong muốn điểm này càng dương càng tốt, còn người chơi cực tiểu lại mong muốn điểm này càng âm càng tốt. Do đó để dễ xử lí ta sẽ tùy theo mức người chơi mà đổi dấu giá trị đánh giá thế cờ pos. Chú ý rằng, thay đổi độ sâu là chuyển sang đối phương nên phải đổi dấu. Chương trình thực hiện đổi dấu như sau:
     value:= -Minimax (pos, depth- 1); {Tính điểm của pos}
Do dùng cùng hàm lượng giá nên khi đến lượt, người chơi cực đại và cực tiểu có cùng cái nhìn như nhau về một thế cờ. Điều này dẫn đến có thể dùng cùng cách chọn nước đi tốt nhất cho họ. Vậy ta phát triển hàm Minimax thành dạng:
function Minimax (pos, depth): integer;
begin
if (depth = 0) or (pos là nút lá) then
    Minimax:= Eval (pos)   {Tính giá trị thế cờ pos}
else
begin
            best := -INFINITY;
            gen(pos); { Sinh ra mọi nước đi từ thế cờ pos}
            while còn lấy được một nước đi m do
            begin
                 pos := Tính thế cờ mới nhờ đi m;
                 value := -Minimax (pos, depth -1);
                if value > best then best := value;
            end;
            Minimax := best;
end;
end;
Trong cài đặt, bàn cờ được biểu diễn bằng các biến toàn cục. Do đó thay cho truyền tham số là một bàn cờ mới pos vào thủ tục Minimax thì người ta biến đổi luôn biến toàn cục này nhờ thược hiện nước đi "thử" (nước đi dẫn đến bàn cờ mới pos). Sau khi Minimax thực hiện việc tính toán dựa vào bàn cờ lưu ở biến toàn cục thì thuật toán sẽ dùng một số thủ tục để loại bỏ nước đi này. Như vậy Minimax bỏ các tham số pos như sau:
function Minimax(depth) :  integer;
begin
if (depth = 0) then
   Minimax := Eval { Tính giá trị thế cờ pos }
else
begin
            best := -INFINITY;
            gen;   {Sinh ra mọi nước đi từ thế cờ pos }
            while (còn lấy được một nước đi m) do
            begin
            thực hiện nước đi m;
            value := -Minimax (depth- 1);
            bỏ thực hiện nước đi m;
            if value > best then best := value;
end;
Minimax := best;
end;
end;

3. Đánh giá
Thuật toán Minimax thăm toàn bộ cây trò chơi bằng việc dùng chiến lược tìm kiếm theo chiều sâu. Nên độ phức tạp của thuật toán này tương ứng trực tiếp với kích thước không gian tìm kiếm bd, trong đó b là hệ số phân nhánh của cây hay chính là nước đi hợp pháp tại mỗi điểm, d là độ sâu tối đa của cây. Thuật toán sẽ thăm tất cả các nút không chỉ là các nút lá vì vậy số lượng các nút được thăm sẽ là b(bd-1)(b-1). Nhưng hàm lượng giá trị sẽ là phương thức chi phối hầu hết thời gian và chỉ làm trên các nút lá, vì vậy việc thăm các nút không phải các nút lá có thể bỏ qua. Do đó độ phức tạp thời gian là O(bd). Bàn chất của thuật tóan là tìm kiếm theo chiều sâu, vì vậy việc đòi hỏi không gian bộ nhớ của nó chỉ tuyến tính với db. Vì thế độ phức tạp không gian là O(bd).
Nếu hệ số nhánh trung bình của cây là b=40, và tìm kiếm đến độ sâu d=4 (các con số thường gặp trong trò chơi cờ) thì số nút phải lượng giá là 404 =2560000 (trên 2 triệu rưỡi nút). Còn với b=40, d=5 thì số nút phải lượng giá sẽ tăng 40 lần nữa thành 405=102400000 (trên 102 triệu nút) đây là con số tương đối lớn.


4. Ứng dụng vào AI cờ tướng
Trò chơi Cờ tướng của nhóm có những chức năng như sau:
- Tạo bàn cờ, có hai chế độ lựa chọn: Người đấu với máy, máy đấu với máy.
- Trong cả hai chế độ, có thể tuy chỉnh “độ cao cờ”, cụ thể là độ sâu xét duyệt của thủ tục Minimax, độ sâu càng lớn, độ khó càng tăng, thời gian tính toán của AI cũng sẽ lâu hơn.
4.1 Lượng giá và sinh nước đi
Như đã nói ở phần xây dựng giải thuật Minimax trong chương hai, việc thiết kế hàm lượng giá eval() và hàm sinh nước đi gen() đối với từng trò chơi là khác nhau, việc thiết kế cũng khác nhau và khá phức tạp, trong phần này, mình sẽ trình bày ngắn gọn về hai hàm này được xây dựng trong trò chơi Cờ tướng.

4.1.1 Lượng giá

Đánh giá một thế cờ là một trong những nhiệm vụ quyết định chương trình chơi cờ của bạn có là "cao thủ" hay không. Căn cứ vào một thế cờ máy sẽ gán cho nó một điểm số (lượng giá tĩnh) để đánh giá độ tốt - xấu. Nhờ điểm này máy mới có thể so sánh các thế cờ với nhau và biết chọn nước đi tốt nhất. Đây là một nhiệm vụ rất khó khăn và phức tạp do không tồn tại một thuật toán tổng quát và thống nhất nào để tính điểm cả. Điểm của một thế cờ dựa trên rất nhiều yếu tố mà khó có thể số hoá hết được, như phụ thuộc vào số lượng và giá trị các quân cờ hiện tại, phụ thuộc vào tính hãm, tính biến, thế công, thế thủ của từng quân cờ cũng như cả cục diện trận đấu. Ví dụ, một cặp Mã giao chân, có thể sát cánh tiến quân và tựa lưng phòng thủ thường có giá hơn hai Mã đứng rời. Nhưng cũng có lúc hai Mã đứng rời lại tốt hơn hai Mã giao chân khi Mã này lại cản Mã kia trong một thế trận nào đó. Ta cũng biết rằng, "lạc nước hai Xe đành bỏ phí, gặp thời một Tốt cũng thành công", thế nhưng số hoá điều này qua hàm lượng giá quả là một điều khó quá sức.
Trong phần này chúng ta chỉ cài đặt phương pháp đơn giản nhưng cơ bản nhất: Lượng giá dựa trên cơ sở giá trị của từng quân cờ. Cách tính này sẽ lấy tổng giá trị các quân cờ hiện có của bên mình trừ đi tổng giá trị các quân cờ hiện có của đối phương. Do đó, một thế cờ này hơn thế cờ kia ở chỗ nó còn nhiều quân bên mình hơn, nhiều quân giá trị cao hơn cũng như có bắt được nhiều quân và quân giá trị cao của đối phương hơn không.
Ta có đoạn giả mã cho hàm lượng giá eval() như sau:
// Mảng 3 chiều POINT_TABLE chứa điểm của các quân quân cờ ở các vị trí cụ thể trên bàn cờ.
   private int eval() {
        int sum = 0;
        for (int i = 0; i < BOARD_SIZE; ++i) {
            if là bên MAX :
               // Tổng điểm ở vị trí quân cờ i, màu i - 1, vị trí thứ i trong bảng POINT_TABLE (tức điểm của nó ở vị trí cụ thể trên bàn cờ)
                sum += POINT_TABLE[piece[i]][color[i] - 1][i];
            else if là bên MIN :
                sum -= POINT_TABLE[piece[i]][color[i] - 1][i];
                 
        }
        return sum + điểm thưởng khi quân cờ ở các vị trí có lợi;
    }

4.1.2 Sinh nước đi
Một trong những việc quan trọng nhất để máy tính có thể chơi được cờ là phải chỉ cho nó biết mọi nước đi có thể đi được từ một thế cờ. Máy sẽ tính toán để chọn nước đi có lợi nhất cho nó. Các yêu cầu chính đối với một thủ tục sinh nước đi là:
·        Chính xác (rất quan trọng). Một nước đi sai sẽ làm hỏng mọi tính toán. Đồng thời chương trình có thể bị trọng tài xử thua luôn. Do số lượng nước đi sinh ra lớn, luật đi quân nhiều và phức tạp nên việc kiểm tra tính đúng đắn tương đối khó.
·        Đầy đủ (rất quan trọng). Sinh được mọi nước đi có thể có từ một thế cờ.
·        Nhanh. Do chức năng này phải sinh được hàng triệu nước đi mỗi khi máy đến lượt nên giảm thời gian sinh nước đi có ý nghĩa rất lớn.
Sinh nước đi là một thuật toán vét cạn. Máy sẽ tìm mọi nước đi hợp lệ có thể có. Máy phải sinh được từ những nước đi rất hay cho đến những nước đi rất ngớ ngẩn (như đẩy Tướng vào vị trí khống chế của đối phương). Ta hình dung ngay thủ tục sinh nước đi Gen sẽ có đầy những vòng lặp for, các câu lệnh kiểm tra if và rẽ nhánh case, trong đó các phép tính kiểm tra giới hạn chiếm một phần đáng kể. Thủ tục này luôn sinh nước cho bên đang tới lượt chơi căn cứ vào nội dung của biến side (trong trương chình, biến side là một biến toàn cục chứa giá trị để thể hiện một bên cờ, thêm một biến xside để thể hiện giá trị của bên còn lại). Đây là một trong những thủ tục phức tạp và dễ sai nhất.
Dưới đây là giả mã cho hàm sinh nước đi gen():
for Xét lần lượt từng quân cờ bên hiện tại do
    case quâncờ of
    Xe: begin
            while Xét lần lượt tất cả các ô từ bên phải Xe cho đến lề trái bàn cờ do
            begin
                if (ô đang xét là ô trống) or (ô đang xét chứa quân đối phương) then
                    gen_push(vị trí của Xe, vị trí ô đang xét)
                    if ô đang xét không trống then break;
                end;

                while Xét lần lượt tất cả các ô từ bên trái Xe cho đến lề phải bàn cờ do
                 ...
                while Xét lần lượt tất cả các ô từ bên trên Xe cho đến lề trên bàn cờ do
                ...
                while Xét lần lượt tất cả các ô từ bên dưới Xe cho đến lề dưới bàn cờ do
                ...
            end; { Xong quân Xe }

                                    Pháo:
                                       ...

Trong đoạn mã giả còn có thủ tục gen_push. Thủ tục gen_push có hai tham số là vị trí xuất phát của quân cờ sẽ đi và đích đến của quân đó. Các nước vừa sinh ra sẽ được đưa vào danh sách nước đi gen_dat nhờ thủ tục gen_push này:
private void gen_push(int from, int dest) {
        if (moveSave(from, dest)) {
            gen_dat[gen_end[ply]].m.from = from;
            gen_dat[gen_end[ply]].m.dest = dest;
            gen_end[ply]++;
        }
    }



Trình bày đến đây chắc các bạn cũng hiểu sơ sơ phải làm như thế nào để ứng dụng thuật toán Minimax vào trò chơi cờ tướng rồi đúng không. Hy vọng các bạn có thể tự làm cho mình một con AI cờ tướng để tự sướng nhé. Mình sẽ tiếp tục trở lại với các bạn vào loạt bài Tìm hiểu về trí tuệ nhân tạo trong các phần sau :)





Comments

Popular posts from this blog

Sử dụng Jedis làm việc với Redis trong Java

Bài viết này mình sẽ   giới thiệu về Jedis , một thư viện client Java cho  Redis . 1. Tại sao lại là Jedis? Redis liệt kê các thư viện client nổi tiếng nhất trên  trang web chính thức  của họ  .  Có nhiều lựa chọn thay thế cho Jedis, nhưng chỉ có hai lựa chọn khác xứng đáng để đề xuất đó là  lettuce  và  Redisson . Hai clients này có một số tính năng độc đáo như an toàn luồng, xử lý kết nối lại trong suốt và API không đồng bộ, tất cả các tính năng mà Jedis thiếu. Tuy nhiên, Jedis nhỏ và nhanh hơn đáng kể so với hai loại kia.  Bên cạnh đó, nó là thư viện client được lựa chọn của các nhà phát triển Spring Framework, và nó có cộng đồng lớn nhất trong cả ba. 2. Maven Dependencies Hãy bắt đầu bằng cách khai báo dependency trong file  pom.xml  : 1 2 3 4 5 < dependency >      < groupId >redis.clients</ groupId >      < artifactId >jedis</ artifactId >      < version >2.8.1</ version > </ dependency >

Sử dụng Jenkins để Build Docker Images

Khởi chạy Jenkins Khởi chạy Jenkins như một Docker Container với lệnh sau: docker run -d -u root --name jenkins \ -p 8080:8080 -p 50000:50000 \ -v /root/jenkins_2112:/var/jenkins_home \ jenkins/jenkins:2.112-alpine Load Dashboard Tên người dùng  admin có mật khẩu mặc định là  344827fbdbfb40d5aac067c7a07b9230 Trên hệ thống của bạn, bạn có thể tìm mật khẩu bằng docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword Có thể mất vài giây để Jenkins hoàn thành việc bắt đầu và có sẵn.  Trong các bước tiếp theo, bạn sẽ sử dụng trang Dashboard để định cấu hình các plugin và bắt đầu tạo Image Docker. Cấu hình Plugin Docker Bước đầu tiên là cấu hình  plugin Docker  .  Plugin này dựa trên plugin Jenkins Cloud.  Khi build Docker Image, nó sẽ tạo ra một "Cloud Agent" thông qua plugin.  Tác nhân sẽ là Docker Container được cấu hình để giao tiếp với Docker Daemon Job build của Jenkins sẽ sử dụng vùng chứa nà