Introduction#
If you haven't completed the introductory task, please refer to the previous article Getting Started with Installation.
Sparring Sorcerers is the first task of the Think Cairo data structure and algorithm task series, focusing on ownership and array operations in Cairo. The task requires writing a function to simulate battles between teams of two sorcerers.
In the file sorcerer.cairo, sorcerers are defined as having attributes such as attack, health, and talent. There are two methods, new and with_talent, for creating instances of sorcerers. The file also includes methods for retrieving the attack and health values, as well as a method to check if the health is zero (indicating failure).
The files sorcerer_duel.cairo and sorcerer_battle.cairo need to be implemented by ourselves.
Part I: Simple Duels#
In the first part, we need to write the duel function duel(ref sorcerer1: Sorcerer, ref sorcerer2: Sorcerer)
in sorcerer_duel.cairo. Each round of the duel starts with sorcerer1 attacking, followed by sorcerer2. The battle stops if either sorcerer's health reaches zero. There is no return value.
We can use the loop{}
construct in Cairo, which is similar to the while loop in other programming languages. This part is relatively easy. In the loop, we reduce the health of the opposing sorcerer by the attack value of the current sorcerer. The loop terminates if either sorcerer's health becomes zero. It is important to note that the health value should not become negative.
Part II: The Talent Mechanic#
In the second part, we need to add talents to the sorcerers. There are three new talents: Venomous, which increases the attack value by 1 each round; Swift, which only inflicts 1 point of damage if the opponent's attack value is less than 4; and Guardian, which can block the first instance of damage.
Note the use of the ref
keyword in the function fn duel(ref sorcerer1: Sorcerer, ref sorcerer2: Sorcerer)
. With ref
, we can directly modify the passed-in parameters and change the state outside the function. Refer to https://book.cairo-lang.org/ch04-02-references-and-snapshots.html?highlight=snapshot#mutable-references for more information.
My approach is to assign the attack value to a mutable variable named damage
, such as let mut damage1 = sorcerer1.attack;
. Another way to read the attack value is by using sorcerer1.attack()
, as the Sorcerer
struct implements the method fn attack(self: @Sorcerer) -> u8 { *self.attack }
. The @
symbol is used to obtain a snapshot, and *
is used to convert the snapshot into a new variable. However, this approach requires importing use src::sorcerer::SorcererImpl;
.
First, we check for the Swift talent. If sorcerer2 has the Swift talent and sorcerer1's attack value is greater than 4, we set damage = 1
. Then we check for the Guardian talent. If sorcerer2 has the Guardian talent, we skip modifying the health value during the battle and change it to Talentless. Finally, we check for the Venomous talent and directly modify sorcerer.attack
by incrementing it by 1 (not damage
).
It is important to pay attention to the comparison method for enums, the use of the mut
keyword, and other parts that are similar to general programming languages. The condition statement in the loop needs to end with a semicolon, otherwise an error may occur stating that the condition cannot be used in the loop.
Part III: Team Battles#
The third part involves team battles, where we need to create two arrays, team1
and team2
. Each sorcerer in the teams takes turns participating in the battle. If a sorcerer's health reaches zero, they are replaced by the next sorcerer in the array. The order of attacks can be determined by referring to the test case test/battle_test_3.cairo
.
Similarly, the function fn battle(ref team1: Array<Sorcerer>, ref team2: Array<Sorcerer>)
uses ref
for the team arrays. The subsequent operations will directly modify the arrays.
The basic idea is to send out the first sorcerer in each team for each battle. If a sorcerer's health reaches zero, they are replaced by the next sorcerer. The battle ends when either team has no more sorcerers or when all the sorcerers in the battle have zero health.
However, directly modifying the elements in the array, even with ref
, will not take effect. We need to use the pop_front
method to remove the element from the array, modify it, and then add it back.
There are many details that need to be adjusted based on the test cases. After passing the local tests, there will be more rigorous tests online, so the code needs to be further improved.
If the test cases cannot be passed, it is recommended to use debugging to step through and identify the reasons.
Conclusion#
The content is starting to become more challenging. I hope you can remain patient. If you get stuck, try referring to other documents, such as https://github.com/starkware-libs/cairo/tree/main/corelib/src and https://book.cairo-lang.org/. With perseverance, you will achieve great results. Keep up the good work!