[Game Maker Adventure] Chương 014: Array – Mảng

Array là một công cụ quan trọng nhờ đặc tính quản lí được một dãy phần tử, các bài toán tối ưu, thuật toán sắp xếp...

Tóm tắt chương trước

Bạn cùng Kurobo khám phá sâu vào phế tích.

Trong phế tích nguy hiểm, Kurobo đã gợi ý bạn viết một chương trình điều khiển hành vi của Kurobo trong trận chiến.

Kiến thức chương này

  • Array là một tập hợp danh sách các biến có cùng kiểu.
  • Vị trí (hay phần tử) thứ n của một array sẽ tương ứng với index [n-1].
  • Để xem trong array có bao nhiêu phần tử ta cần sử dụng property Length.
  • Để sao chép Array, ta không thể dùng dấu gán.

Cốt truyện

“Cuối cùng cũng hoàn thành!!!”

“Chúc mừng Chủ nhân đã hoàn thành được 50% quãng đường!”

“…”

“?”

“50%?”

“Đúng vậy, chủ nhân quên mình phải thiết lập cho Kurobo sao?”

“À…”

Array (mảng) là gì?

Hãy giả sử bạn cần lưu một danh sách top những người chơi giỏi nhất trong game của mình.

Thông thường, mỗi vị trí sẽ cần một biến điểm và một biến tên.

int ptsTop1;
int ptsTop2;
int ptsTop3;
//...
int ptsTop100;

string nameTop1;
string nameTop2;
string nameTop3;
//...
string nameTop100;

Với một bảng xếp hạng (BXH) dài 100 trăm người, chẳng lẽ phải tạo ra 200 biến tương ứng?

Sẽ ra sao nếu ta muốn có BXH cho toàn server?

Thật may, Array là một trong những công cụ giúp ta xử lí vấn đề đó.

Array là một tập hợp danh sách các biến có cùng kiểu. Chẳng hạn, ta có thể viết gọn đoạn code trên lại như sau:

int[] ptsTop = new int[100];
string[] nameTop = new string[100];

Hàng trăm dòng giờ đã được rút gọn thành 2 dòng!

*Khai báo Array

Khai báo – tức declare – là quá trình thiết lập và đặt tên vùng nhớ

*Kurobo nhắc lại chương 3

Thông thường, array được khai báo theo công thức:

<<type>>[] + <<arrayName>> = new + <<type>>[<<số lượng phần tử>>];

Trong đó:

<<type>> là kiểu dữ liệu của các biến trong Array. Array chỉ có thể lưu một danh sách biến có cùng kiểu. Một Array kiểu int[] sẽ không thể lưu các biến khác int như string, float, bool…

<<arrayName>> là tên của Array, cách đặt tên giống cách đặt tên biến.

new là từ khoá bắt buộc khi khai báo các type phức tạp như array, list, struct, class… new có tác dụng tạo một vùng nhớ mới với độ lớn X phần tử.

<<số lượng phần tử>> là số lượng phần tử có trong Array. Chẳng hạn int[] scores = new int[10]; nghĩa là array scores chứa 10 phần tử kiểu int.

Tại thời điểm này, C# sẽ tạo trước 10 vị trí nhớ dành cho array scores. Các vị trí nhớ được tạo đó sẽ mang trong mình các giá trị mặc định. Về bản chất, ngay tại bước này array đã được khởi tạo.

Ngoài ra, khi một array đã được khai báo, ta không thể thay đổi số lượng phần tử chứa trong nó. Tuy nhiên, ta có thể xoá toàn bộ array cũ để tạo một array mới với từ khoá new.

Chẳng hạn:

//score 10 phan tu
int[] scores = new int[10];

//score 100 phan tu
scores = new int[100];

Lúc này, C# sẽ xoá toàn bộ 10 phần tử của array ban đầu để tạo ra 100 phần tử mới. Chúng ta sẽ nghiên cứu sâu hơn phần này khi vào các chương thuật toán.

*Khởi tạo Array

Khởi tạo – tức initializing – là hành động gán giá trị cho biến

*Kurobo nhắc lại chương 3

Để gán giá trị cho từng vị trí nhớ hay từng phần tử của Array, ta cần sử dụng indexer operator ([ ] – đóng mở ngoặc vuông). Sau đó thực hiện gán (=) giống như biến thông thường.

Thí dụ:

int[] scores = new int[10];

scores[0] = 9928;
scores[1] = 9102;

Nằm giữa 2 dấu ngoặc vuông được gọi là index. Index là số chỉ ra vị trí vùng nhớ trong Array.

Vị trí đầu tiên của array được đánh index bằng 0.

Vị trí thứ 2 có index là 1.

Vị trí thứ 3 có index là 2.

Cứ như vậy, vị trí (hay phần tử) thứ n của một array sẽ tương ứng với index [n-1].

Ngoài ra, ta không thể truy cập vượt quá phạm vi của một array. Chẳng hạn, array với 10 phần tử thì không thể truy cập vào vị trí thứ 100, vị trí thứ 11 được.

Khi cố gắng truy cập vượt quá phạm vi, trình biên dịch ngay lập tức báo lỗi Index Out Of Range.

Các kiểu khai báo

Dưới đây là một số kiểu khai báo array thường được sử dụng.

Style 1

int[] playerIDs = new int[5];
string[] playerNames = new string[5];

//ID
playerIDs[0] = 102;
playerIDs[1] = 51;
playerIDs[2] = 126;
playerIDs[3] = 22;
playerIDs[4] = 1112;

//Name
playerNames[0] = "A";
playerNames[1] = "B";
playerNames[2] = "C";
playerNames[3] = "D";
playerNames[4] = "E";

Đây là kiểu thường được sử dụng, bởi tính đơn giản, rõ ràng.

Style 2

int[] playerIDs = new int[] { 102, 51, 126, 22, 1112 };
string[] playerNames = new string[] { "A", "B", "C", "D", "E" };

Không cần xác định số phần tử.

Style 3

int[] playerIDs = { 1, 2, 3, 4, 5, 6 };
string[] playerNames = { "A", "B", "C", "D", "E" };

Không cần từ khoá new, không cần xác định số phần tử.

Tuỳ từng trường hợp mà bạn có thể sử dụng bất kì kiểu nào, các kiểu gán lúc khởi tạo (style 2, style 3) thường sẽ sử dụng để load config khi mới vào game (vị trí các điểm spawn, danh sách quái, bản đồ…), style 1 thường dùng trong các bài toán có tính cập nhật (BXH, Slot đồ, Buff List…).

Length

Để xem trong array có bao nhiêu phần tử ta cần sử dụng [su_tooltip title=”property” text=”Giống với biến nhưng nhiều tính năng hơn.”]property[/su_tooltip] Length.

Chẳng hạn

int[] playerIDs = { 1, 2, 3, 4, 5, 6 };
Console.WriteLine(playerIDs.Length);

Màn hình sẽ cho ra kết quả 6 do playerIDs có 6 phần tử.

Length thường được sử dụng để làm điều kiện thoát vòng loop khi muốn xét duyệt tất cả các phần tử trong array.

Thí dụ

Trong game, khi mở một chiếc hòm, ta có thể in ra màn hình danh sách các món đồ trong nó như sau:

string[] items = new string[] 
{ 
    "Key", "Bottle", "A piece of paper", "5 Gold Coin", "A rat" 
};

for (int i = 0; i < items.Length; i++)
{
    Console.WriteLine(items[i]);
}

Kết quả:

items.Length trả về số lượng phần tử trong items – tức là 5.

Index của items bắt đầu từ 0 nên index chỉ có thể mang giá trị lớn nhất là 4 (tương ứng với phần tử thứ 5 của items).

Chú ý

Trường hợp dưới đây sẽ gây lỗi Index Out Of Range.

for (int i = 0; i <= items.Length; i++)
{
    Console.WriteLine(items[i]);
}

Sử dụng loop trong mảng để xét duyệt tất cả các phần tử có trong tủ đồ.

Sao chép Array

Để sao chép biến, bạn có thể dùng dấu gán (=)

int a = 5;
int b = 1;

a = b; //a = 1

Nhưng để sao chép Array, ta không thể dùng dấu gán như vậy

int[] array1 = new int[] { 30, 40, 59 };
int[] array2 = array1;

array1[0] = 9999;

Console.WriteLine(array2[0]);

Kết quả:

9999

Rõ ràng ta chỉ thay đổi dữ liệu ở array1 nhưng tại sao array2 cũng bị thay đổi theo?

Điều này là do Array chỉ ghi nhớ vị trí dữ liệu (hay còn gọi là Reference Value – sẽ bàn sâu hơn ở những chương cuối). Khi gán array2 = array1; tức là ta chỉ gán vị trí dữ liệu của chúng.

Do đó, nếu ta thay đổi phần tử của array1 thì phần tử trong array2 cũng thay đổi theo.

Giống như việc cả hai người dùng chung một cuốn sách vậy. Nếu cuốn sách bị viết bậy thì cả hai đều ảnh hưởng.

Ta có thể sao chép array bằng cách kết hợp từ khoá new, for loop và Length:

int[] array1 = new int[] { 30, 40, 59 };
int[] array2 = new int[array1.Length];

for (int i = 0; i < array1.Length; i++)
{
    array2[i] = array1[i];
}

array1[0] = 9999;

Console.WriteLine(array2[0]);

Về cơ bản, ta tạo một array2 mới với số phần tử bằng array1. Sau đó gán tất cả các phần tử tương ứng của array1 sang array2.

Kết quả:

30

Quest: Kurobo Upgrade!

Bạn và Kurobo tìm thấy một khu vực trống trải.

Bạn nhìn chằm chằm vào những hình nhân quái vật kì dị.

Chúng không có phản ứng gì.

Dường như, đây là một bãi tập luyện. Một công đôi việc, vừa hay, bạn cũng muốn nâng cấp Kurobo để tăng sức chiến đấu cho mình.

Quest: Lại vào thế giới cổ thư! ✦✦✧✧✧

Đi được một đoạn, cuốn cổ thư đột nhiên bay từ trong túi bạn.

Bạn chưa kịp định thần thì đã bị kéo vào không gian quen thuộc ấy.

[ays_quiz id=”7″]

Quest: Kurobo Upgrade! ✦✦✦✦✧

“Người trở về rồi hả?”

“… so với lần trước thì Kurobo có vẻ bình tĩnh nhỉ?”

“Thì tại, Kurobo có hốt hoảng cũng không giải quyết được gì.”

“Mà, dù sao thì chủ nhân mau vào khu tập luyện thôi!”

***

Để khiến Kurobo có thể chiến đấu, ngoài thiết lập các chỉ số cơ bản, phần khó khăn nhất là khiến Kurobo tấn công kẻ địch có lượng máu thấp nhất.

Hãy viết tiếp chương trình dựa trên các gợi ý ban đầu, thoả mãn:

  1. Sử dụng chương trình gốc ban đầu để khai triển (xem bên dưới).
  2. Phe địch gồm 3 hình nhân quái vật. Phe ta gồm bạn và Kurobo.
  3. Chỉ số cơ bản của các nhân vật gồm: Máu (HP), Tốc độ (SPD) và Công (ATK). Thông số chi tiết giống hình.
  4. Trận chiến diễn ra theo lượt. Ai có chỉ số SPD cao sẽ được ưu tiên đánh trước.
  5. Khi thay thế chỉ số Tốc độ (SPD) khởi điểm, thứ tự turn order cũng phải thay đổi động theo config ban đầu.
  6. Ngay khi bắt đầu lượt, Kurobo được ưu tiên tấn công trước, không cần biết SPD của nó là bao nhiêu.
  7. Trong lượt của Kurobo, nó tự động tấn công kẻ địch ít máu (HP) nhất.
  8. Trong lượt của bạn, bạn được lựa chọn tấn công kẻ địch nào.
  9. Kẻ địch chỉ tấn công bạn (Kurobo không thể bị tấn công).
  10. Trận chiến kết thúc khi 1 trong 2 phe hoàn toàn bị hạ gục.

Chương trình gốc cần được khôi phục:

string[] enemiesName = new string[] { "Dummy A", "Dummy B", "Dummy C" };

int[] enemiesHP = new int[] { 400, 200, 500 };
int[] enemiesATK = new int[] { 40, 90, 60 };
int[] enemiesSPD = new int[] { 100, 50, 88 };

int yourHP = 600;
int yourATK = 140;
int yourSPD = 77;

int kuroboATK = 60;

bool isEnd = false;
int currentTurn = 1;

Gợi ý thành phẩm:

Xem đáp án

[mycred_sell_this]

Khi code các chương trình phức tạp, đầu tiên hãy viết Flow đơn giản.

Sau đó khai triển tiếp từng block bên trong

Sau khi có Flow có thể tính toán sơ qua bản thân bạn cần gì và sắp xếp cấu trúc code như thế nào.

Flow càng chi tiết càng tốt, nhưng không nên mất quá nhiều thời gian ở bước này. Tốt nhất hãy sử dụng Flow như guideline tổng quan.

Đoạn code đầu tiên sẽ sử dụng comment để ghi chú những phần cần phải làm trong flow:

string[] enemiesName = new string[] { "Dummy A", "Dummy B", "Dummy C" };

int[] enemiesHP = new int[] { 400, 200, 100 };
int[] enemiesATK = new int[] { 50, 90, 60 };
int[] enemiesSPD = new int[] { 100, 50, 88 };

int yourHP = 600;
int yourATK = 140;
int yourSPD = 77;

int kuroboATK = 60;

bool isEnd = false;
int currentTurn = 1;

//sort

//gameplay
while (!isEnd)
{
    //battle info
    
    //kurobo atk first
    
    //battle
}

Khai triển //sort

Đây là giai đoạn quan trọng nhất. Tại phần này, ta sẽ sắp xếp lại danh sách tốc độ (SPD) của các nhân vật theo thứ tự từ cao xuống thấp.

Ý tưởng sắp xếp là tạo một array mới có số phần tử là tất cả các nhân vật trong battle. Kurobo không có SPD, không có HP, không bị tấn công nên trong battle chỉ có: You (bạn), Dummy A, Dummy B, Dummy C tham chiến.

Ta tạo 5 array có công dụng như sau:

  • sortedSpds – tốc độ của các nhân vật trong battle (đã sắp xếp)
  • sortedName – tên của các nhân vật trong battle
  • sortedATK – tấn công của các nhân vật trong battle
  • sortedHP – lượng HP của các nhân vật trong battle
  • sortedIsEnemy – ứng với từng nhân vật trong các danh sách trên là địch hay bạn

5 array này sẽ mang thông tin của 1 nhân vật trong battle nếu có cùng index. Chẳng hạn sortedSpds[0], sortedName[0], sortedATK[0], sortedHP[0], sortedIsEnemy[0] đều thuộc về một nhân vật ở index 0.

Điểm mấu chốt thứ 2 là phương pháp sort.

Ở đây ta sử dụng phương pháp Selection Sort (sẽ nói thêm trong Series Thuật Toán).

Selection Sort được miêu tả là một phương pháp xét duyệt tất cả các phần tử trong danh sách ban đầu để tìm ra phần tử có giá trị lớn nhất. Sau đó, sao chép phần tử vừa tìm được vào một array mới và set giá trị của phần tử đó ở danh sách ban đầu về giá trị thấp nhất. Lặp lại cho đến khi array mới được hoàn thành.

Chẳng hạn, ta có array gốc có 3 phần tử: int[] arrayGoc = { 9, 2, 8 };

  • Tạo 1 array mới có cùng số phần tử với array cần sắp xếp: int[] arraySorted = new int[3];
  • Ta tìm phần tử lớn nhất của array gốc: 9. Gán 9 vào arraySorted[0].
  • Sau đó set giá trị của arrayGoc[0] thành giá trị thấp nhất trong tất cả các giá trị. VD: -1000. Trong arrayGoc lúc này sẽ là {-1000, 2, 8}. Trong arraySorted sẽ là {9, ?, ?}.
  • Làm tương tự, ta tìm được 8 và 2. arraySorted lúc này sẽ trở thành {9, 8, 2}.

Áp dụng tất cả các điều trên, ta có đoạn code sắp xếp tốc độ của nhân vật như sau:

//sort
int[] spds = new int[enemiesSPD.Length + 1];
string[] names = new string[enemiesSPD.Length + 1];
int[] atks = new int[enemiesSPD.Length + 1];
int[] hps = new int[enemiesSPD.Length + 1];
bool[] isEnemy = new bool[enemiesSPD.Length + 1];

for (int i = 0; i < enemiesSPD.Length; i++)
{
    spds[i] = enemiesSPD[i];
    names[i] = enemiesName[i];
    atks[i] = enemiesATK[i];
    hps[i] = enemiesHP[i];
    isEnemy[i] = true;
}

spds[spds.Length-1] = yourSPD;
names[names.Length - 1] = "You";
atks[atks.Length - 1] = yourATK;
hps[hps.Length - 1] = yourHP;
isEnemy[isEnemy.Length - 1] = false;


int[] sortedSpds = new int[spds.Length];
string[] sortedName = new string[spds.Length];
int[] sortedATK = new int[spds.Length];
int[] sortedHP = new int[spds.Length];
bool[] sortedIsEnemy = new bool[spds.Length];

for (int sortIndex = 0; sortIndex < sortedSpds.Length; sortIndex++)
{
    int indexOfSpdsPos = 0;
    int maxSpd = 0;
    for (int i = 0; i < spds.Length; i++)
    {
        if (maxSpd < spds[i])
        {
            maxSpd = spds[i];
            indexOfSpdsPos = i;
        }
    }
    sortedSpds[sortIndex] = maxSpd;
    sortedName[sortIndex] = names[indexOfSpdsPos];
    sortedATK[sortIndex] = atks[indexOfSpdsPos];
    sortedHP[sortIndex] = hps[indexOfSpdsPos];
    sortedIsEnemy[sortIndex] = isEnemy[indexOfSpdsPos];

    spds[indexOfSpdsPos] = -1;
}

Sau đó, xét mỗi phần tử của array đó

Khai triển //battle info

Dựa vào danh sách có phải Enemy hay không để in ra danh sách team địch, team mình lên màn hình.

//battle info
    Console.WriteLine("================");

    Console.WriteLine("---Enemy Team---");
    for (int i = 0; i < sortedSpds.Length; i++)
    {
        if (sortedIsEnemy[i] && sortedHP[i] > 0)
        {
            Console.WriteLine(sortedName[i] + " - HP: " + sortedHP[i] + " (Press " + i + " to attack!)");
        }
    }
    Console.WriteLine("---Your Team---");
    for (int i = 0; i < sortedSpds.Length; i++)
    {
        if (!sortedIsEnemy[i])
        {
            Console.WriteLine(sortedName[i] + " - HP: " + sortedHP[i] + " | ATK: " + sortedATK[i]);
        }
    }
    Console.WriteLine("Kurobo (Passive) - HP: ??? | ATK: " + kuroboATK);

Khai triển //kurobo atk first

Đây là logic tấn công cho Kurobo.

//kurobo atk first
    int minHPForKurobo = int.MaxValue; //int.MaxValue is a biggest number of an int
    int attackIndex = 0;
    for (int i = 0; i < sortedHP.Length; i++)
    {
        if (minHPForKurobo > sortedHP[i] && sortedIsEnemy[i] && sortedHP[i] > 0)
        {
            minHPForKurobo = sortedHP[i];
            attackIndex = i;
        }
    }
    if (sortedHP[attackIndex] > 0)
    {
        Console.WriteLine("Kurobo ATK and deal " + kuroboATK + " DMG to " + sortedName[attackIndex]);

        sortedHP[attackIndex] -= kuroboATK;
        if (sortedHP[attackIndex] <= 0)
        {
            sortedHP[attackIndex] = 0;
            Console.WriteLine(sortedName[attackIndex] + " is dead.");
        }
    }

Khai triển //battle

Phần này về logic auto của kẻ địch và lựa chọn kẻ địch nào để tấn công.

Dựa vào sortedIsEnemy để xem lượt của nhân vật đó là địch hay ta và đưa ra các quyết định khác nhau.

//battle
    for (int i = 0; i < sortedSpds.Length; i++)
    {
        if (sortedIsEnemy[i] && sortedHP[i] > 0)
        {
            Console.WriteLine(">-" + sortedName[i] + " turn-<");
            for (int j = 0; j < sortedIsEnemy.Length; j++)
            {
                if (!sortedIsEnemy[j])
                {
                    sortedHP[j] -= sortedATK[i];
                    Console.WriteLine(sortedName[i] + " attacked you.");
                    Console.WriteLine("Your current HP: " + sortedHP[j]);
                }
            }
        }
        else if (!sortedIsEnemy[i])
        {
            //choose enemy to attack
            Console.WriteLine("+-Your turn-+");
            Console.WriteLine("Choose enemy to attack");
            int enemySelected = Convert.ToInt32(Console.ReadLine());
            sortedHP[enemySelected] -= yourATK;
            Console.WriteLine("You deal " + yourATK + " to " + sortedName[enemySelected]);
            if (sortedHP[enemySelected] <= 0)
            {
                sortedHP[enemySelected] = 0;
                Console.WriteLine(sortedName[enemySelected] + " is dead.");
            }
        }
    }

Ghép và Hoàn thiện

Sau khi đã thêm bớt một chút, ta có thành phẩm như bên dưới.

string[] enemiesName = new string[] { "Dummy A", "Dummy B", "Dummy C" };

int[] enemiesHP = new int[] { 400, 200, 100 };
int[] enemiesATK = new int[] { 50, 90, 60 };
int[] enemiesSPD = new int[] { 100, 50, 88 };

int yourHP = 600;
int yourATK = 140;
int yourSPD = 77;

int kuroboATK = 60;

bool isEnd = false;
int currentTurn = 1;

//sort
int[] spds = new int[enemiesSPD.Length + 1];
string[] names = new string[enemiesSPD.Length + 1];
int[] atks = new int[enemiesSPD.Length + 1];
int[] hps = new int[enemiesSPD.Length + 1];
bool[] isEnemy = new bool[enemiesSPD.Length + 1];

for (int i = 0; i < enemiesSPD.Length; i++)
{
    spds[i] = enemiesSPD[i];
    names[i] = enemiesName[i];
    atks[i] = enemiesATK[i];
    hps[i] = enemiesHP[i];
    isEnemy[i] = true;
}

spds[spds.Length-1] = yourSPD;
names[names.Length - 1] = "You";
atks[atks.Length - 1] = yourATK;
hps[hps.Length - 1] = yourHP;
isEnemy[isEnemy.Length - 1] = false;


int[] sortedSpds = new int[spds.Length];
string[] sortedName = new string[spds.Length];
int[] sortedATK = new int[spds.Length];
int[] sortedHP = new int[spds.Length];
bool[] sortedIsEnemy = new bool[spds.Length];

for (int sortIndex = 0; sortIndex < sortedSpds.Length; sortIndex++)
{
    int indexOfSpdsPos = 0;
    int maxSpd = 0;
    for (int i = 0; i < spds.Length; i++)
    {
        if (maxSpd < spds[i])
        {
            maxSpd = spds[i];
            indexOfSpdsPos = i;
        }
    }
    sortedSpds[sortIndex] = maxSpd;
    sortedName[sortIndex] = names[indexOfSpdsPos];
    sortedATK[sortIndex] = atks[indexOfSpdsPos];
    sortedHP[sortIndex] = hps[indexOfSpdsPos];
    sortedIsEnemy[sortIndex] = isEnemy[indexOfSpdsPos];

    spds[indexOfSpdsPos] = -1;
}

//gameplay
while (!isEnd)
{
    //battle info
    GetBattleInfo();
    //kurobo atk first
    int minHPForKurobo = int.MaxValue; //int.MaxValue is a biggest number of an int
    int attackIndex = 0;
    for (int i = 0; i < sortedHP.Length; i++)
    {
        if (minHPForKurobo > sortedHP[i] && sortedIsEnemy[i] && sortedHP[i] > 0)
        {
            minHPForKurobo = sortedHP[i];
            attackIndex = i;
        }
    }
    if (sortedHP[attackIndex] > 0)
    {
        Console.WriteLine("Kurobo ATK and deal " + kuroboATK + " DMG to " + sortedName[attackIndex]);

        sortedHP[attackIndex] -= kuroboATK;
        if (sortedHP[attackIndex] <= 0)
        {
            sortedHP[attackIndex] = 0;
            Console.WriteLine(sortedName[attackIndex] + " is dead.");
        }
    }

    //check battle is ended?
    int enemyLife = 0;
    int allyLife = 0;
    for (int i = 0; i < sortedHP.Length; i++)
    {
        if (sortedIsEnemy[i]) enemyLife += sortedHP[i];
        else allyLife = sortedHP[i];
    }
    if (enemyLife <= 0)
    {
        isEnd = true;
        Console.WriteLine("You win!");
        break;
    }
    else if (allyLife <= 0)
    {
        isEnd = true;
        Console.WriteLine("You lose!");
        break;
    }

    GetBattleInfo();
    Console.WriteLine("----------");


    //battle
    for (int i = 0; i < sortedSpds.Length; i++)
    {
        if (sortedIsEnemy[i] && sortedHP[i] > 0)
        {
            Console.WriteLine(">-" + sortedName[i] + " turn-<");
            for (int j = 0; j < sortedIsEnemy.Length; j++)
            {
                if (!sortedIsEnemy[j])
                {
                    sortedHP[j] -= sortedATK[i];
                    Console.WriteLine(sortedName[i] + " attacked you.");
                    Console.WriteLine("Your current HP: " + sortedHP[j]);
                }
            }
        }
        else if (!sortedIsEnemy[i])
        {
            //choose enemy to attack
            Console.WriteLine("+-Your turn-+");
            GetBattleInfo();
            Console.WriteLine("Choose enemy to attack");
            int enemySelected = Convert.ToInt32(Console.ReadLine());
            sortedHP[enemySelected] -= yourATK;
            Console.WriteLine("You deal " + yourATK + " to " + sortedName[enemySelected]);
            if (sortedHP[enemySelected] <= 0)
            {
                sortedHP[enemySelected] = 0;
                Console.WriteLine(sortedName[enemySelected] + " is dead.");
            }
        }
        GetBattleInfo();
    }
    currentTurn++;
}

void GetBattleInfo()
{
    Console.WriteLine();
    Console.WriteLine("Press any key to continue...");
    Console.ReadKey();
    Console.Clear();
    //turn count
    Console.WriteLine("Current Turn: " + currentTurn);
    //turn order
    Console.WriteLine("Turn Order: ");
    for (int i = 0; i < sortedSpds.Length; i++)
    {
        if (sortedHP[i] > 0)
        Console.WriteLine(" (SPD: " + sortedSpds[i] + ") - " + sortedName[i]);
    }
    //battle info
    Console.WriteLine("================");

    Console.WriteLine("---Enemy Team---");
    for (int i = 0; i < sortedSpds.Length; i++)
    {
        if (sortedIsEnemy[i] && sortedHP[i] > 0)
        {
            Console.WriteLine(sortedName[i] + " - HP: " + sortedHP[i] + " (Press " + i + " to attack!)");
        }
    }
    Console.WriteLine("---Your Team---");
    for (int i = 0; i < sortedSpds.Length; i++)
    {
        if (!sortedIsEnemy[i])
        {
            Console.WriteLine(sortedName[i] + " - HP: " + sortedHP[i] + " | ATK: " + sortedATK[i]);
        }
    }
    Console.WriteLine("Kurobo (Passive) - HP: ??? | ATK: " + kuroboATK);
    Console.WriteLine("================");
    Console.WriteLine();
}

Đoạn code ngày càng dài rồi nhỉ?

Cách giải của mình chỉ để tham khảo. Trong lập trình, để giải quyết 1 vấn đề có rất nhiều cách khác nhau, vì thế nếu cách giải của bạn không giống mẫu thì cũng đừng lo lắng quá

Nếu thấy có thắc mắc, xin hãy comment ngay xuống bên dưới hoặc báo bên discord nhé.

[/mycred_sell_this]

Chương Trước | Tổng quan | Chương Kế

Kurrwo

Công nhân👷gõ 💬 văn bản ⌨️ trò chơi 🎮 điện tử 📱 👏👏👏

Post navigation