Becoming a Ruby Warrior With Artificial Intelligence
The following is a guest post by Carlos Lazo and originally appeared onĀ his blog.Ā Carlos is currently a student at The Flatiron School. You can follow him on TwitterĀ here.
In Weeks 3 ā 6 at Flatiron School, the focus has been on learning both the Sinatra and Rails web frameworks. Understanding the paradigms has been crucial in spinning up web applications āthe right wayā.
However, itās important to remember the foundation on which these frameworks are built ā theĀ RubyĀ language. With only 6 weeks of Ruby knowledge under my belt, I want to continue understanding the principles of abstraction, modeling, and scope.
Enter the realm ofĀ Artificial Intelligence (AI).
Iāve had the honor and pleasure of working with AI concepts in the Scheme programming language. I wanted to explore this realm in Ruby, and it turns out thereās a great venue. Before I get into that, let me define two common terms used in AI:
Agent: an autonomous entity which observes through sensors and acts upon an environment using actuators and directs its activity towards achieving goals.
Heuristic: a function that ranks alternatives in various search algorithms at each branching step based on the available information in order to make a decision about which branch to follow during a search.
Hereās a cool image that will make these definitions clearer:
Now with that background, ontoĀ Ruby Warrior.
TheĀ Ruby WarriorĀ project (GithubĀ andĀ Ruby Gem) was built as a vehicle to teach Ruby. How? Through the gamification of artificial intelligence.
Hereās a quick overview:
You (the player) are a Warrior in this world, with your primary objective being to scale levels of a tower.
There is a beginner / intermediate tower, both with āepicā modes.
Each level is laid out differently, and can have a variety of components.
Monsters, Captives, Bombs, Walls, etc.
Each level grants the warrior more abilities.
You get to perform one and only one action(!) per turn based on whatever logic you choose to define.
More abilities lead to harder levels (e.g. more directions to move in).
A score is given per level based on different things: level clear speed, amount of action! used, captives rescued, etc.
Every turn, theĀ play_turnĀ method is called inĀ player.rbĀ file ā this and any other files can be used, as long asĀ play_turnĀ calls one and only one action.
For this post, my goal is to share how Iāve applied my Ruby skills to the first 4 levels. Hereās a quick legend regarding level layouts:
Legend: Tower Level Symbols
Here is the representation of Level 01:
This level is pretty straightforward. Having only one action available [warrior.walk!], the logic here is simple:
Level 01 was completed and I achieved maximum points.
Model heuristic functionality based on immediate sufficiency.
Here is the representation of Level 02:
This level introduced the first monster. I realized I needed to add logic to check to see if a monster was in front of me based on my available actions. Still pretty straightforward.
Level 02 was completed now completed.
Decision logic is going to get completed quick.
Probably worth refactoring and āsetting the stageā in Level 03.
Able to make assumption thatĀ PlayerĀ class is being initialized one time, withĀ play_turnbeing called in a loop. Take advantage of theĀ initializeĀ method.
Here is the representation of Level 03:
Four (4) monsters.Ā Oh snap son.
As I began writing my code, I realized I didnāt want to do annoying amounts of nested logic. Projecting into the future, I felt the need to begin splitting parts of the agent into logical methods in an organized structure. I also needed to figure out when was the right time to rest, to keep moving forward, and when to attack.
Overall, I was really happy with my code ā beat this level with no issues. Even though it grew in size, theĀ play_turnĀ method is readable and tells me exactly what the Warrior is to do at any given point in time.
(+) Breaking out logic into well-named functions was a great idea!
(ā) Potential issues in the future with additional functionality (like more actions).
(ā) Donāt like how each function needs to haveĀ warriorĀ as a parameter.
Can this be fixed with instance variables in Level 04?
(ā) ALL actions are evaluated even if an action is already called.
Given all the negatives, there was going to be some heavy-duty refactoring in Level 04. All in all though, I was fairly certain that the logic in the code was ājust going to workā.
I couldnāt have been more wrong.
Here is the representation of Level 04:
Enter the dreadedĀ ArcherĀ ā umm⦠f*ck.
This unit can attack from multiple spaces away. With my current logic, Iād rest when the space in front of me was empty and I wasnāt in combat.Ā BUT I WAS IN COMBAT, since my health was decreasing by 1HP even though I was resting (rest = +2HP, attack = -3HP).
This now forced new state logic into my methods, along with some well-needed refactoring.
Awesome! This now gets around the ādistance attackā issue. If Iām being attacked from afar and the space in front of me is empty,Ā do not restĀ and continue walking until you find and slay the offending monster.
(+) Building modular code makes it easy to add in edge cases.
(+) Constant refactoring makes for better flow.
(ā)Ā class PlayerĀ is getting huge.
Consider splitting things into separate classes?
Interestingly enough, your Agent can always be āmore intelligentā.
I have a long way to go to reach the top of theĀ BeginnerĀ tower, but this has been a tremendous learning experience. Iāve been able to apply Ruby principles to the challenging yet fun problem space of Artificial Intelligence.
A few things Iām thinking about going forward:
Classes and further simplification makes sense.
The levels are only going to get harder:
Ability to move in different directions.
How will I track movement?
How will I behave when I hit a wall?
Commanding a āgolemā during my turn.
Avoiding bombs that detonate.
Iām convinced that my upfront work will help prevent the following from happening:
Slowly but surely, I will become the Ruby Warrior Iām destined to be.
A shoutout to my boyĀ Dan FriedmanĀ for working on this with me.
Thereās still much learning to do and more levels to conquer. Onward and upward!