Tutorial 16

ðŸ§Đ

Object-Oriented Programming - OOP

9618 A-Level


Note that OOP is an A2 topic - it's not required for AS

Object-Oriented Programming (OOP) a programming paradigm that allows both attributes (data) and methods (behaviour - i.e. functions/procedures (modules)) to be stored as a single object. A class is a template, but that class will be instantiated (created) at runtime and can hence be different (i.e. contain different attribute values) from other instances of the same class

Before we begin, let's list some basic definitions of some concepts this tutorial will cover - these will be restated again with more details and with relevant examples later in this tutorial, but it may be useful to refer to this initial list throughout and at the end as a check for your understanding

  • Class: a user-defined template used to model attributes (data) and methods (behaviour - i.e. class procedures/functions) about some aspect of a program (e.g. a game character) - a class in itself isn't executed, specific instances have to be instantiated (created)
  • Attribute: a variable contained within a class, much like a record data structure contains attributes/properties/fields too
  • Methods: the name for a class module (procedure/function) - this ability to have executable code as part of the class is where classes provide more functionality than a record
  • Encapsulation: the concept that both attributes (data) and methods (behaviour) can be contained within a single class object - this makes sense, when you consider that to "encapusulate" means to "fully enclose" something (data and behaviour), as if in a capsule
  • Constructor: this is the default method (and specifically, a constructor should be a procedure) that is called automatically when the class is instantiated - constructors must be defined as follows PUBLIC PROCEDURE NEW(...params)
  • Instantiation: the act of creating a class instance (object) - e.g. we could describe the code bmw <- NEW Car("bmw", 2005) as "instantiating (creating) an instance of the Car class (and passing "bmw" and 2005 into its constructor)"
  • Object: the name for an instance of a class - in the example above, the object would be the bmw - we could then access attributes/methods belonging to that object with dot . syntax, like when accessing record attributes
  • Access: both attributes and methods can have an access level of either PRIVATE (accessible only from within the class) or PUBLIC - accessible from both within the class and outside, via the object - e.g. syntax like bmw.driveTo(50, 30). PUBLIC is the default access level, if none is specified (though it's best to)
  • Getters: a method to return a (usually private) attribute from a class - getters can also be more advanced - e.g. calculating some new value based on other attributes/random values etc
  • Setters: a method to assign to (set) a (presumably private) attribute value from outside the class - this has benefits including allowing validation and not setting the value if the validation fails
  • Inheritance: the concept that a child class will have access to the methods and attributes that exist in any parent classes - there can be multiple levels of inheritance (e.g. class C could inherit from class B, which inherits from class A) and is achieved with the INHERITS keyword in pseudocode
  • Polymorphism: the word literally means "many forms" or "many shapes" - hence this is when an inheriting class can either use the method defined in the super (parent) class or its overridden version of that method defined in either it or another parent class (if e.g. having 3 levels of inheritance where C inherits from B and B from A as mentioned above - if we try to access a method/attribute that doesn't exist in C, then first B, then A will be checked for a match (i.e. going up the class hierarchy from the leaf to the root class))
  • Super: the name for the parent that a particular class inherits from - if a child class has overridden a method, we can still call the super class's method with the super keyword SUPER.MethodName()

Additionally, there is also a video tutorial that covers OOP for A2

1

Constructors & Instantiation

By itself, a class is just a template - we have to create specific instance(s) of classes to actually use them. This process of creating an instance is called instantiation and is achieved by the NEW keyword

A constructor is the default method called when we create an instance of a class - note how we have to declare the constructor inside that class with PUBLIC PROCEDURE NEW - we will look at access later in the tutorial, but briefly, PUBLIC means that attributes/methods can be accessed from both in and outside the class, while PRIVATE attributes or methods can only be accessed from inside the class

In the example below, we create 2 instances of the Person class (bill and ted). Currently, nothing much happens - we are not storing any any data (attributes) and don't have any behaviour (methods) associated with them

CLASS Person PUBLIC PROCEDURE NEW() OUTPUT "Created person!" ENDPROCEDURE ENDCLASS p1 ← NEW Person() p2 ← NEW Person()

2

Attributes

Attributes are data (variables/arrays/records/sets/even other classes etc) that can be stored as part of the class object

A class with just attributes is effectively just a record

This helps with organisation - i.e. data can be grouped together and stored as one object, rather than, for example, many different arrays

Note how rather than using the DECLARE keyword, we instead use the access modifier (PUBLIC or PRIVATE) for declaration

You can see we also pass 2 attribute values as parameters to the constructor - I have prefixed these parameters with a "p" - e.g. "pName", so that the website is able to distinguish the parameter variable pName from the class attribute Name - in other languages, this is achieved with keywords like this or self if referring to the class attribute, rather than a parameter/global/local variable. As you can see in the code, the 2 arguments (parameter values) are assigned to the class attribute

Note that we don't have to assign to a class attribute from a parameter in the constructor - hunger for example is initialised with a default value 50 which we have chosen, while the attribute friend is currently uninitialised and can be set at a later point in the program

CLASS Person PUBLIC name : STRING PUBLIC dateOfBirth : DATE PUBLIC hunger : INTEGER PUBLIC friend : Person PUBLIC PROCEDURE NEW(pName : STRING, pDateOfBirth : DATE) name ← pName dateOfBirth ← pDateOfBirth hunger ← 50 OUTPUT "Created person called ", name, " born on ", dateOfBirth ENDPROCEDURE ENDCLASS bill ← NEW Person("Bill", 16/7/1965) ted ← NEW Person("Ted", 2/9/1964)

3

Methods

A method is a simply a function or procedure encapsulated (contained) within a class - a few things to note:

  • Like attributes, methods need to be declared with an access keyword PUBLIC or PRIVATE
  • As always, we use CALL to call a procedure, but not a function
  • The constructor is a required method and should be a procedure with the identifier (name) NEW
  • Methods can be called inside the class directly - e.g. CalculateAge() inside the OutputDetails() method
  • Or, if they are public, they can be called from outside the class on the instance object with the dot notation - e.g. napolean.CalculateAge()
  • You can also see CALL is used to call the OutputDetails() method on each of the 4 person instances
CLASS Person PUBLIC name : STRING PUBLIC dateOfBirth : DATE PUBLIC hunger : INTEGER PUBLIC friend : Person PUBLIC PROCEDURE NEW(pName : STRING, pDateOfBirth : DATE) name ← pName dateOfBirth ← pDateOfBirth hunger ← 50 ENDPROCEDURE PUBLIC PROCEDURE OutputDetails() OUTPUT name, " was born on ", dateOfBirth, " - they are ", CalculateAge(), " years old and their hunger is ", hunger ENDPROCEDURE PUBLIC FUNCTION CalculateAge() RETURNS INTEGER DECLARE todaysDate : DATE DECLARE age : INTEGER todaysDate ← TODAY() age ← YEAR(todaysDate) - YEAR(dateOfBirth) IF MONTH(todaysDate) < MONTH(dateOfBirth) OR (MONTH(todaysDate) = MONTH(dateOfBirth) AND DAY(todaysDate) < DAY(dateOfBirth)) THEN age ← age - 1 ENDIF RETURN age ENDFUNCTION ENDCLASS bill ← NEW Person("Bill", 16/7/1965) ted ← NEW Person("Ted", 2/9/1964) genghis ← NEW Person("Genghis Khan", 15/11/1162) napolean ← NEW Person("Napolean", 15/8/1769) CALL bill.OutputDetails() CALL ted.OutputDetails() CALL genghis.OutputDetails() CALL napolean.OutputDetails() OUTPUT napolean.CalculateAge()

4

Access

As has been hinted at, there are two access modifiers that Cambridge pseudocode supports

  • PUBLIC: attribute or method can be accessed from anywhere - this is the default if no access modifier is specified
  • PRIVATE: attribute or method can be accessed only from inside the class

Note: the site also supports accessing private attributes from a subclass for convenience, without needing getters everywhere - most languages don't support this, so in paper 4, you will probably need to use a getter to access a private attribute defined in a class' ancestor (e.g. parent, grandparent class etc)

Below we can see a contrived example with both a public and private attribute and method - this code includes 2 access violations which will result in the following errors. We would have to remove these statements for the code to compile/execute

  • Can't access private identifier "y" on class instance test
  • Can't access private identifier "Multiply" on class instance test

For public, there is really just one advantage - convenience. You can access attributes/methods from anywhere, without having to create additional getters/setters (see section after this)

In contrast, the benefits of making attributes private include:

  • Restricted access: other programmers can't access private attributes/methods from outside the class (e.g. different files) (unless they change them back to public), hence are less likely to modify important internal workings of a class that hence introduce in bugs
  • Validation: we can add setters that include validation checks - for example, rather than allowing a programmer to set an email directly, you could have a setter method that first ensures the email is a valid format before assigning it to the class attribute, displaying an error if invalid
  • Hides complexity: imagine you have a class to encode videos to a specific format - internally, the class might 100s of complex attributes/methods that would confuse other developers trying to use this class - instead, they could simply be presented with a simple public method that takes in the input filename & type, then the desired output type/filename/quality etc - all the complex encoding attributes/methods are kept private (hence don't show in IDE code suggestions), with the developer instead just seeing this simple public method

A common misconception is that making an attribute or method private means it is more secure - i.e. less easy to view/modify by malicious parties. This is NOT true - e.g. storing passwords or credit card details but making them private still wouldn't be secure, since the data is stored unencrypted in RAM regardless

  • To an end user, they would be able to inspect RAM contents (data and code) with memory scanners/debuggers/disassemblers either way, hence can view/modify it regardless of it it's public or private
  • For a programmer working on the project, they will have access to the source code, hence could just modify the private attributes/methods to be public, add output statements to display the private attributes etc
CLASS AccessTest PUBLIC x : INTEGER PRIVATE y : INTEGER PUBLIC PROCEDURE NEW(pX : INTEGER, pY : INTEGER) x ← pX y ← pY OUTPUT "Created: ", x, ", ", y OUTPUT "Addition (Constructor): ", Addition() OUTPUT "Multiply (Constructor): ", Multiply() ENDPROCEDURE PUBLIC FUNCTION Addition() RETURNS INTEGER RETURN x + y ENDFUNCTION PRIVATE FUNCTION Multiply() RETURNS INTEGER RETURN x * y ENDFUNCTION ENDCLASS test ← NEW AccessTest(4, 5) OUTPUT "-----" OUTPUT test.x OUTPUT test.y //access error OUTPUT "Addition (Outside): ", test.Addition() OUTPUT "Multiply (Outside): ", test.Multiply() //access error

5

Getters & Setters

Related to the concept of access, we might not want a user to directly access or modify class attributes - that is where a getter comes in

Here are some use cases of getters:

  • Read-only attributes - this can be achieved by making attributes private to disable direct modification, but still allowing a programmer to get the value of that attribute via a public getter - see the dateOfBirth example below, since we don't want to allow the person's date of birth to be changed
  • Additional logic - we could have a more complex calculation in a getter - for example, suppose we only want the person's year of birth, rather than the whole date. Rather than copying and pasting the same code every time we want to do that, we can instead create a simple GetYear getter - this is a simple example, but for a more complex example, this elimination of code duplication means there will be less bugs, code can more easily be modified since it only has to be change in one place etc
  • Logging - suppose we have personal data like emails - we might want to record each time the field was accessed, the staff who accessed it etc for attribution in the future (e.g. abnormal access amounts/misuse/data leaks etc)
  • Debugging - it might be easier to set a breakpoint/print out additional data every time we try to access an attribute in order to help us find & fix bugs

Likewise, a setter is a method that allows us to set (assign) to an attribute - there are again a few use cases:

  • Validation - suppose we are creating a game and a person's hunger can be within a range 0-100. Each hour, their hunger could increase by 5, while eating food could reduce the hunger by a given amount - their hunger should always be in the range 0-100 though. A range check could hence be performed inside the setter and the value could be clamped within this range
  • Logging - as above, if we have important or personal data, we might want to store every time it changed. Many websites now record the date & time, IP address, browser details and more each time a password is updated, for example - to act as a record in future in case malicious modifications need to be investigated
  • Debugging - as above, it might be easier to log e.g. the before/after value when setting, various other important data with the goal of making finding & fixing bugs easier
  • Deferred/Lazy Initialisation - the friend instance might not exist at the time that particular person was created, hence we wouldn't be able to assign it in the constructor, but can assign it in later via a setter (though we could also assign to the attribute directly if it's public too)

Note: in some exams, you will be told either in sentence-form or via a class diagram whether to make an attribute public or private as well as what getters & setters to create - if not, it's often preferred/required in the mark scheme that attributes are private.

Tip: since this site is aimed at practice and in paper 3, you will never have to write more than 1 or 2 getters and setters max, the site hence requires you manually write them - however, for many modern IDEs, if you simply declare the attributes, you will then be able to have the IDE automatically generate the getters and setters for you. This is of course very useful if you have large numbers of attributes. In JetBrains IDEs (IntelliJ, Pycharm etc), this can be achieved by right clicking, selecting "generate" from the dropdown box, then choosing getters/setters, the constructor or whatever else you want

CLASS Person PRIVATE name : STRING PRIVATE email : STRING PRIVATE dateOfBirth : DATE PRIVATE hunger : INTEGER PRIVATE friend : Person PUBLIC PROCEDURE NEW(pName : STRING, pEmail : STRING, pDateOfBirth : DATE) name ← pName email ← pEmail dateOfBirth ← pDateOfBirth hunger ← 50 ENDPROCEDURE PUBLIC PROCEDURE OutputDetails() OUTPUT name, " (", email, ") was born on ", dateOfBirth, " - they are ", CalculateAge(), " years old and their hunger is ", hunger ENDPROCEDURE PUBLIC FUNCTION CalculateAge() RETURNS INTEGER DECLARE todaysDate : DATE DECLARE age : INTEGER todaysDate ← TODAY() age ← YEAR(todaysDate) - YEAR(dateOfBirth) IF MONTH(todaysDate) < MONTH(dateOfBirth) OR (MONTH(todaysDate) = MONTH(dateOfBirth) AND DAY(todaysDate) < DAY(dateOfBirth)) THEN age ← age - 1 ENDIF RETURN age ENDFUNCTION PUBLIC FUNCTION GetDateOfBirth() RETURNS DATE RETURN dateOfBirth ENDFUNCTION PUBLIC FUNCTION GetEmail() RETURNS STRING OPENFILE PersonAccessLog.txt FOR APPEND WRITEFILE PersonAccessLog.txt, email & " was read on " & TODAY() CLOSEFILE PersonAccessLog.txt RETURN name ENDFUNCTION PUBLIC FUNCTION GetYearOfBirth() RETURNS INTEGER RETURN YEAR(dateOfBirth) ENDFUNCTION PUBLIC FUNCTION GetName() RETURNS STRING RETURN name ENDFUNCTION PRIVATE PROCEDURE SetHunger(changeAmount : INTEGER) hunger ← hunger + changeAmount IF hunger < 0 THEN hunger ← 0 ELSE IF hunger > 100 THEN hunger ← 100 OUTPUT name, " died of starvation :(" ENDIF ENDIF ENDPROCEDURE PUBLIC PROCEDURE SetEmail(pEmail : STRING) email ← pEmail OUTPUT "Updated email for ", name, " to ", pEmail ENDPROCEDURE PUBLIC PROCEDURE SetFriend(BYREF pFriend : Person) friend ← pFriend OUTPUT name, " has a friend named ", pFriend.GetName() ENDPROCEDURE PUBLIC PROCEDURE EatFood(foodName : STRING, energy : INTEGER) DECLARE hungerPrev : INTEGER hungerPrev ← hunger CALL SetHunger(-energy) IF hungerPrev > energy THEN OUTPUT name, " ate ", foodName, " and their hunger reduced by ", energy, " to ", hunger ELSE OUTPUT name, " ate ", foodName, " and their hunger only reduced by ", hungerPrev, " to ", hunger, " since they weren't that hungry" ENDIF ENDPROCEDURE ENDCLASS bill ← NEW Person("Bill", "bill.esquire@msn.com", 16/7/1965) ted ← NEW Person("Ted", "ted.logan@aol.com", 2/9/1964) genghis ← NEW Person("Genghis Khan", "g.khan@hotmail.com", 15/11/1162) napolean ← NEW Person("Napolean", "napolean@gmail.com", 15/8/1769) CALL bill.OutputDetails() OUTPUT "--- Getter Tests ---" OUTPUT ted.GetEmail() OUTPUT genghis.GetDateOfBirth() OUTPUT napolean.GetYearOfBirth() OUTPUT "--- Setter Tests ---" CALL napolean.EatFood("ice cream", 20) //calls the private SetHunger method internally CALL napolean.EatFood("marshmellows", 10) CALL napolean.EatFood("steak", 40) CALL napolean.SetEmail("president@france.gov") CALL bill.SetFriend(ted)

6

Inheritance, Super & Polymorphism

Some of the most important aspects of OOP are the following concepts:

  • Inheritance: a hierarchical class structure can be created whereby the child class inherits (has access to) the methods and attributes in the parent (super) classes. The INHERITS keyword is used to define this in pseudocode, as seen in the example below. It's important to note that the first statement inside an inheriting class' constructor must be a call to the parent class' constructor with the code CALL SUPER.NEW(...)
  • Super: this refers to the parent class that other classes might inherit from. If we specify SUPER, then it means refer to the method or attribute in the parent class, rather than in the current class
  • Polymorphism: if you break down the word, "poly" means "many" and "morph" means "shape" or "form" - generally, you can see that means a class can have multiple behaviours, or, more specifically, it is when a child class overrides the behaviour of the super class (yet the programmer has access to both - e.g. in the example below, calling OutputDetails() in either the Teacher or Footballer class will call the method in those classes respectively (which provide more information, related to that particular person type), while calling SUPER.OutputDetails() will call the method in the parent Person class

In the example below, you can see we have the following 3 classes:

Person

  • Contains name and role attributes, along with getters, setters and an OutputDetails method that outputs those two attributes that all classes that inherit from Person will also have access to

Teacher

  • Takes additional subject arguments in its constructor and calls the parent class' constructor immediately
  • Updates the role inside the constructor for all teachers from "npc" to "teacher" - this follows the principle of DRY (don't repeat yourself) - i.e. we only need this one statement, rather than e.g. requiring the programmer to manually pass "teacher" into the constructor for every teacher they create, which is error prone and unnecessarily/tediously duplicates code
  • Is polymorphic - OutputDetails is different from the OutputDetails in the super class, in that it also outputs the teacher's subject - the difference between them is demonstrated by calling both methods in the constructor as an example

Footballer

  • As above, introduces its own attributes, getters and setters, polymorphic OutputDetails method etc

Hopefully these class examples demonstrate the advantage of OOP for specific situations - imagine extending this for a game for example. All people might have common methods like Eat, Run, Sleep, Talk etc and attributes like Name, Speed, Items, Hunger etc - yet each different character type would also have their own methods and attributes too

CLASS Person PRIVATE name, role : STRING PUBLIC PROCEDURE NEW(pName : STRING) name ← pName role ← "npc" ENDPROCEDURE PUBLIC PROCEDURE OutputDetails() OUTPUT name, " is a ", role ENDPROCEDURE PUBLIC PROCEDURE SetName(pName : STRING) name ← pName ENDPROCEDURE PUBLIC FUNCTION GetName() RETURNS STRING RETURN name ENDFUNCTION PUBLIC PROCEDURE SetRole(pRole : STRING) role ← pRole ENDPROCEDURE PUBLIC FUNCTION GetRole() RETURNS STRING RETURN role ENDFUNCTION ENDCLASS CLASS Teacher INHERITS Person PRIVATE subject : STRING PUBLIC PROCEDURE NEW(pName, pSubject : STRING) CALL SUPER.NEW(pName) CALL SUPER.SetRole("teacher") subject ← pSubject CALL SUPER.OutputDetails() //calls OutputDetails in SUPER (parent) class CALL OutputDetails() //calls OutputDetails in this class if it exists, else goes up one level in inheritance hierachy until it finds it ENDPROCEDURE PUBLIC PROCEDURE OutputDetails() OUTPUT name, " is a ", role, " who teaches ", subject ENDPROCEDURE PUBLIC PROCEDURE SetSubect(pSubject : STRING) subject ← pSubject ENDPROCEDURE PUBLIC FUNCTION GetSubject() RETURNS STRING RETURN subject ENDFUNCTION ENDCLASS CLASS Footballer INHERITS Person PRIVATE team, position : STRING PUBLIC PROCEDURE NEW(pName, pTeam, pPosition : STRING) CALL SUPER.NEW(pName) CALL SUPER.SetRole("footballer") team ← pTeam position ← pPosition ENDPROCEDURE PUBLIC PROCEDURE OutputDetails() OUTPUT name, " is a ", role, " who plays as a ", position, " for ", team ENDPROCEDURE PUBLIC PROCEDURE SetTeam(pTeam : STRING) team ← pTeam ENDPROCEDURE PUBLIC FUNCTION GetTeam() RETURNS STRING RETURN team ENDFUNCTION PUBLIC PROCEDURE SetPosition(pPosition : STRING) position ← pPosition ENDPROCEDURE PUBLIC FUNCTION GetPosition() RETURNS STRING RETURN position ENDFUNCTION ENDCLASS alice ← NEW Person("Alice") CALL alice.OutputDetails() carl ← NEW Teacher("Carl", "Astrophysics") ronaldo ← NEW Footballer("Ronaldo", "Man Utd", "winger") CALL ronaldo.OutputDetails() CALL ronaldo.SetName("C7") CALL ronaldo.SetTeam("Al-Nassr") CALL ronaldo.OutputDetails()

It's important to note that can can have more than just 1 level of inheritance - for example, in this contrived example, we can see that C inherits from both B and A (since B inherits from A, therefore since C inherits from B, it also inherits from A too)

When calling the Hello() method in class C, since this method doesn't exist in class C, but does exist in the immediate parent, then both calls will execute the method defined in class B

In contrast, since Goodbye() is only defined in class A, then a call to it from C will bubble all the way up to class A, where a method with this name is found

CLASS A PUBLIC num : INTEGER PUBLIC PROCEDURE NEW(pNum : INTEGER) OUTPUT "Constructor - A" num ← pNum ENDPROCEDURE //named Addition to avoid warning of custom ADD operator for sets already existing PUBLIC FUNCTION Addition() RETURNS INTEGER RETURN num + num ENDFUNCTION PUBLIC PROCEDURE Hello() OUTPUT "Hello" ENDPROCEDURE PUBLIC PROCEDURE Goodbye() OUTPUT "Goodbye" ENDPROCEDURE ENDCLASS CLASS B INHERITS A PUBLIC PROCEDURE NEW(pNum : INTEGER) CALL SUPER.NEW(pNum) OUTPUT "Constructor - B" ENDPROCEDURE PUBLIC FUNCTION Subtract() RETURNS INTEGER RETURN num - num ENDFUNCTION PUBLIC PROCEDURE Hello() OUTPUT "Hi!" ENDPROCEDURE ENDCLASS CLASS C INHERITS B PUBLIC PROCEDURE NEW(pNum : INTEGER) CALL SUPER.NEW(pNum) OUTPUT "Constructor - C" CALL Hello() CALL SUPER.Hello() CALL Goodbye() ENDPROCEDURE PUBLIC FUNCTION Multiply() RETURNS INTEGER RETURN num * num ENDFUNCTION ENDCLASS a ← NEW A(5) OUTPUT a.Addition() OUTPUT "---" b ← NEW B(5) OUTPUT b.Addition() OUTPUT b.Subtract() OUTPUT "---" c ← NEW C(5) OUTPUT c.Addition() OUTPUT c.Subtract() OUTPUT c.Multiply()

7

Shapes

Create a program with a Shape class that has private width & height attributes for all shapes - there should be getters for these 2 attributes too

You should then create 3 shapes that inherit from this class, with the following methods:

  • Rectangle: GetPerimeter(), GetArea(), IsSquare() //returns Boolean
  • Right-angled triangle: GetPerimeter(), GetArea(), IsPythagoreanTriplet() //returns true if INTEGER values of lengths A and B also give an INTEGER value of the hypotenuse C
  • Circle: GetCircumference(), GetDiameter(), GetArea(), GetSectorArea(degrees), GetArcLength(degrees)

You might wish to create additional attributes or helper methods too

Test your program by creating instances of each shape, calling the different methods etc

CLASS Shape PRIVATE width : REAL PRIVATE height : REAL PUBLIC PROCEDURE NEW(pWidth : REAL, pHeight : REAL) width ← pWidth height ← pHeight ENDPROCEDURE PUBLIC FUNCTION GetWidth() RETURNS REAL RETURN width ENDFUNCTION PUBLIC FUNCTION GetHeight() RETURNS REAL RETURN height ENDFUNCTION ENDCLASS CLASS Rectangle INHERITS Shape PUBLIC PROCEDURE NEW(pWidth, pHeight : REAL) CALL SUPER.NEW(pWidth, pHeight) ENDPROCEDURE PUBLIC FUNCTION GetPerimeter() RETURNS REAL RETURN (width * 2) + (height * 2) ENDFUNCTION PUBLIC FUNCTION GetArea() RETURNS REAL RETURN width * height ENDFUNCTION PUBLIC FUNCTION IsSquare() RETURNS BOOLEAN RETURN width = height ENDFUNCTION ENDCLASS CLASS RightAngledTriangle INHERITS Shape PUBLIC PROCEDURE NEW(base : REAL, height : REAL) CALL SUPER.NEW(base, height) ENDPROCEDURE PUBLIC FUNCTION GetPerimeter() RETURNS REAL DECLARE hypotenuse : REAL hypotenuse ← (width * width + height * height) ** 0.5 RETURN width + height + hypotenuse ENDFUNCTION PUBLIC FUNCTION GetArea() RETURNS REAL RETURN 0.5 * width * height ENDFUNCTION PUBLIC FUNCTION IsPythagoreanTriplet() RETURNS BOOLEAN RETURN IsInteger(width) AND IsInteger(height) AND IsInteger(GetHypotenuse()) ENDFUNCTION PRIVATE FUNCTION GetHypotenuse() RETURNS REAL RETURN (width * width + height * height) ** 0.5 ENDFUNCTION ENDCLASS CLASS Circle INHERITS Shape PRIVATE PI : REAL PUBLIC PROCEDURE NEW(radius : REAL) CALL SUPER.NEW(radius, radius) PI ← 3.141592653589793 ENDPROCEDURE PUBLIC FUNCTION GetCircumference() RETURNS REAL RETURN 2 * PI * width ENDFUNCTION PUBLIC FUNCTION GetDiameter() RETURNS REAL RETURN 2 * width ENDFUNCTION PUBLIC FUNCTION GetArea() RETURNS REAL RETURN PI * width * width ENDFUNCTION PUBLIC FUNCTION GetSectorArea(degrees : REAL) RETURNS REAL RETURN (degrees / 360) * PI * width * width ENDFUNCTION PUBLIC FUNCTION GetArcLength(degrees : REAL) RETURNS REAL RETURN (degrees / 360) * 2 * PI * width ENDFUNCTION ENDCLASS FUNCTION IsInteger(x : INTEGER) RETURNS BOOLEAN RETURN x MOD 1 = 0 ENDFUNCTION OUTPUT "--- Rectangle --- " rectangle ← NEW Rectangle(5, 4) OUTPUT "Perimeter: ", rectangle.GetPerimeter() OUTPUT "Area: ", rectangle.GetArea() OUTPUT "IsSquare: ", rectangle.IsSquare() OUTPUT "" OUTPUT "--- Right-Angled Triangle ---" triangle ← NEW RightAngledTriangle(3, 4) OUTPUT "Perimeter: ", triangle.GetPerimeter() OUTPUT "Area: ", triangle.GetArea() OUTPUT "IsPytagoreanTriplet: ", triangle.IsPythagoreanTriplet() OUTPUT "" OUTPUT "--- Circle --- " circle ← NEW Circle(10) OUTPUT "Diameter: ", circle.GetDiameter() OUTPUT "Circumference: ", circle.GetCircumference() OUTPUT "Area: ", circle.GetArea() OUTPUT "Sector Area: ", circle.GetSectorArea(90) OUTPUT "Arc Length: ", circle.GetArcLength(90)

8

Theme Hospital

Our goal is to a make a very simple simulation of the class game Theme Hospital - the exact implementation, class hierarchy, methods, attributes etc will be left to you to figure out - a great way to practice & solidify understanding of OOP is designing the implementation yourself afterall

The features should include

  • Multiple types of people - e.g. Doctors and Patients - you can give them a random name from this array of 50 sample names: ["Florence", "Hippolyta", "Louis", "Marie", "James", "Francis", "Edward", "Joseph", "Clara", "Sigmund", "Pox", "Dribble", "Gurney", "Scalpel", "Stitch", "Suture", "Thermy", "Vaccy", "Bloaty", "Wheezy", "Sneezey", "Iggy", "Patch", "Saline", "Hemlock", "Abscess", "Gloop", "Mumpsy", "Banting", "Lister", "Curie", "Salk", "Fleming", "Clive", "Edith", "Matilda", "Oliver", "Hannah", "Theodore", "Josephine", "Albert", "Louisa", "Freya", "Simon", "Esther", "Maxwell", "Helena", "Nigel", "Dorothy", "Rupert"]
  • An input prompt asking how many doctors to hire should be displayed at the start of the program
  • A small number of diseases - e.g. Coronavirus, Swine Flue and Social Media Addiction. Each disease will take a given number of days to treat - or, if uncured after a certain number of days, the patient will die
  • A random number of patients (each with one random disease) should arrive each day (up to a maximum of 10 patients in the hospital - otherwise they will be refused, due to the hospital being full)
  • A doctor can treat up to 3 patients each day

Note: since pseudocode only supports fixed-size arrays and there is no concept of null values, you will have to think about how to loop through an array of 10 patients without errors - an approach could be:

  • Store a variable containing the number of patients - only loop up to that point, to prevent getting errors if trying to call methods/access attributes on an array index that has no patient
  • When patients are cured or die, that hospital bed should now become open for another patient - you could store a flag like InHospital on each patient to represent whether this spot can be taken by a new patient

Relevant messages should be output - e.g. an example of the program could be (note this example doesn't show all days - just some where notable events occurred):

DECLARE numDoctors : INTEGER OUTPUT "How many doctors would you like to hire?" INPUT numDoctors TYPE DiseaseRecord DECLARE name : STRING DECLARE daysToTreat, daysTillDeath : INTEGER ENDTYPE DECLARE patients : ARRAY[1:10] OF Patient DECLARE doctors : ARRAY[1:numDoctors] OF Doctor DECLARE names : ARRAY[1:50] OF STRING DECLARE dayNum, index, numPatients : INTEGER names ← ["Florence", "Hippolyta", "Louis", "Marie", "James", "Francis", "Edward", "Joseph", "Clara", "Sigmund", "Pox", "Dribble", "Gurney", "Scalpel", "Stitch", "Suture", "Thermy", "Vaccy", "Bloaty", "Wheezy", "Sneezey", "Iggy", "Patch", "Saline", "Hemlock", "Abscess", "Gloop", "Mumpsy", "Banting", "Lister", "Curie", "Salk", "Fleming", "Clive", "Edith", "Matilda", "Oliver", "Hannah", "Theodore", "Josephine", "Albert", "Louisa", "Freya", "Simon", "Esther", "Maxwell", "Helena", "Nigel", "Dorothy", "Rupert"] dayNum ← 0 numPatients ← 0 FUNCTION GetRandomName() RETURNS STRING RETURN names[INT(RAND(50)) + 1] ENDFUNCTION FUNCTION GetRandomDiease() RETURNS DiseaseRecord DECLARE num : INTEGER num ← INT(RAND(3)) DECLARE disease : DiseaseRecord CASE OF num 0: disease.name ← "Coronavirus" disease.daysToTreat ← 7 disease.daysTillDeath ← 10 1: disease.name ← "Swine Flue" disease.daysToTreat ← 3 disease.daysTillDeath ← 12 2: disease.name ← "Social Media Addiction" disease.daysToTreat ← 5 disease.daysTillDeath ← 20 ENDCASE RETURN disease ENDFUNCTION CLASS Person PRIVATE name : STRING PUBLIC PROCEDURE NEW() name ← GetRandomName() ENDPROCEDURE PUBLIC FUNCTION GetName() RETURNS STRING RETURN name ENDFUNCTION ENDCLASS CLASS Doctor INHERITS Person PUBLIC PROCEDURE NEW() CALL SUPER.NEW() OUTPUT "🧑ðŸŧ‍⚕ïļ Doctor ", name, " was hired" ENDPROCEDURE PUBLIC PROCEDURE TreatPatient(BYREF patient : Patient) OUTPUT "🧑ðŸŧ‍⚕ïļ Doctor ", name, " treated ", patient.GetName() CALL patient.Treat() ENDPROCEDURE ENDCLASS CLASS Patient INHERITS Person PRIVATE disease : DiseaseRecord PRIVATE inHospital, wasTreatedToday : BOOLEAN PUBLIC PROCEDURE NEW() CALL SUPER.NEW() disease ← GetRandomDiease() OUTPUT "😷 Patient ", name, " arrived with ", disease.name inHospital ← TRUE wasTreatedToday ← FALSE ENDPROCEDURE PUBLIC PROCEDURE NextDay() IF inHospital = TRUE THEN disease.daysTillDeath ← disease.daysTillDeath - 1 IF disease.daysTillDeath <= 0 THEN OUTPUT "💀 ", name, " has died of ", disease.name inHospital ← FALSE ELSE IF disease.daysToTreat <= 0 THEN OUTPUT "✅ ", name, " has been cured of ", disease.name inHospital ← FALSE ELSE DECLARE extraMessage : STRING IF wasTreatedToday = FALSE THEN extraMessage ← ". ❗ No doctor was able to treat them today" ELSE extraMessage ← "" ENDIF OUTPUT "⏱ïļ ", name, " is suffering from ", disease.name, " - they require ", disease.daysToTreat, " days of treatment or will die in ", disease.daysTillDeath, " days", extraMessage ENDIF ENDIF ENDIF wasTreatedToday ← FALSE ENDPROCEDURE PUBLIC FUNCTION GetInHospital() RETURNS BOOLEAN RETURN inHospital ENDFUNCTION PUBLIC PROCEDURE Treat() disease.daysToTreat ← disease.daysToTreat - 1 wasTreatedToday ← TRUE ENDPROCEDURE ENDCLASS FOR index ← 1 TO numDoctors doctors[index] ← NEW Doctor() NEXT index WHILE TRUE DO dayNum ← dayNum + 1 OUTPUT "--- Day ", dayNum, " ---" DECLARE numNewPatients, newPatientIndex, treatPatientIndex, numDoctorTreated : INTEGER DECLARE addedNewPatient : BOOLEAN numNewPatients ← INT(RAND(3)) newPatientIndex ← 1 addedNewPatient ← FALSE FOR index ← 1 TO numNewPatients WHILE newPatientIndex <= LENGTH(patients) AND addedNewPatient = FALSE DO IF newPatientIndex > numPatients THEN // OUTPUT "Added patient at index ", newPatientIndex patients[newPatientIndex] ← NEW Patient() addedNewPatient ← TRUE numPatients ← numPatients + 1 ELSE IF patients[newPatientIndex].GetInHospital() = FALSE THEN // OUTPUT "Added patient at index ", newPatientIndex patients[newPatientIndex] ← NEW Patient() addedNewPatient ← TRUE ENDIF ENDIF newPatientIndex ← newPatientIndex + 1 ENDWHILE addedNewPatient ← FALSE NEXT index treatPatientIndex ← 1 FOR index ← 1 TO numDoctors numDoctorTreated ← 0 WHILE numDoctorTreated < 3 AND treatPatientIndex <= numPatients DO IF patients[treatPatientIndex].GetInHospital() = TRUE THEN CALL doctors[index].TreatPatient(patients[treatPatientIndex]) numDoctorTreated ← numDoctorTreated + 1 ENDIF treatPatientIndex ← treatPatientIndex + 1 ENDWHILE NEXT index FOR index ← 1 TO numPatients CALL patients[index].NextDay() // OUTPUT patients[index].GetName() NEXT index OUTPUT "" CALL WAIT(3000) ENDWHILE

9

Pokemon

For the final challenge, we will implement Pokemon battles - the following is suggested:

  • The game will be 2 player - both should be prompted to enter their name
  • The Pokemon and Pokemon move files as the bottom of the page will be loaded
  • There will be 4 types (normal, fire, water, grass) - both Pokemon and each move will have a given type
  • Both players will be able to choose 2 Pokemon (real Pokemon games have 6 - you can choose that if you want - I just chose 2 since the battles are over quicker/it's faster to test)
  • Each Pokemon will start with 200 health (hp/hit points)
  • Each player will be prompted asking which Pokemon they'd like to send into battle first - show relevant information like the Pokemon type, move details etc
  • Player 1 will choose which of the 4 moves they'd like to use - the damage would then be inflicted on player 2's current Pokemon
  • The total damage is calculated based on 3 factors: the move's base damage, the move type and the receiving Pokemon's type. For example, a fire move attacking a grass Pokemon will inflict more (e.g. double) damage, while a fire move attacking a water Pokemon will inflict less (e.g. half) damage. The exact modification multipliers are up to you - in the real Pokemon game, there are around 20 types. As a hint, a 2D array could be a good way to store this
  • If player 2's Pokemon hasn't fainted, it will then have the oppertunity to attack player 1's Pokemon - these alternate attacks continue until a Pokemon has 0 health
  • When a Pokemon has 0 health, the player will be allowed to pick another Pokemon from their pack - this continues until all of a player's Pokemon have fainted, at which point the other player wins

Note: as can be seen in the file at the bottom of the page, the Rest move heals your own Pokemon (assuming it hasn't already fainted), rather than attacking the enemy

CONSTANT MAX_POKEMON = 2 CONSTANT ALL_POKEMON = 20 CONSTANT NUM_MOVES = 30 TYPE TypeRecord = (normal, fire, water, grass) TYPE Move DECLARE name : STRING DECLARE moveType : TypeRecord DECLARE damage, quantity : INTEGER ENDTYPE DECLARE index, pokemonIndex, pokemonChoice : INTEGER DECLARE tempName : STRING DECLARE players : ARRAY[1:2] OF Player DECLARE allPokemon : ARRAY[1:ALL_POKEMON, 1:6] OF STRING DECLARE allMoves : ARRAY[1:NUM_MOVES] OF Move DECLARE gameOver : BOOLEAN DECLARE damageModifiers : ARRAY[1:4, 1:4] OF REAL damageModifiers ← [ [1, 1, 1, 1], [1, 0.5, 0.5, 1.5], [1, 1.5, 0.5, 0.5], [1, 0.5, 1.5, 0.5] ] CALL LoadPokemonFromFile() CALL LoadMovesFromFile() CALL DisplayAllStartingPokemon() CALL EnterNamesAndChoosePokemon() CALL PlayGame() PROCEDURE PlayGame() DECLARE playerIndex, enemyPlayerIndex, moveChoice : INTEGER gameOver ← FALSE FOR playerIndex ← 1 TO 2 CALL players[playerIndex].ShowPokemon() CALL players[playerIndex].ChoosePokemon() NEXT playerIndex CALL CLEAR() WHILE gameOver = FALSE DO enemyPlayerIndex ← 2 FOR playerIndex ← 1 TO 2 IF gameOver = FALSE THEN OUTPUT "--- ", players[playerIndex].name, "'s Move ---" OUTPUT "" CALL players[playerIndex].Attack(players[enemyPlayerIndex]) ENDIF enemyPlayerIndex ← 1 NEXT playerIndex ENDWHILE ENDPROCEDURE CLASS Player PUBLIC name : STRING PUBLIC pokemon : ARRAY[1:MAX_POKEMON] OF Pokemon PUBLIC activePokemonIndex : INTEGER PUBLIC PROCEDURE NEW(pName : STRING) name ← pName ENDPROCEDURE PUBLIC PROCEDURE ShowPokemon() DECLARE index : INTEGER OUTPUT "" OUTPUT "--- ", name, "'s Pokemon ---" OUTPUT "" FOR index ← 1 TO MAX_POKEMON OUTPUT pokemon[index].name IF pokemon[index].health > 0 THEN OUTPUT "ðŸŸĒ ", pokemon[index].GetInfo(index) OUTPUT pokemon[index].GetMoveInfo(FALSE) ELSE OUTPUT "ðŸ”ī ", index, ") ", pokemon[index].name, " fainted" OUTPUT "" ENDIF NEXT index ENDPROCEDURE PUBLIC PROCEDURE ChoosePokemon() REPEAT OUTPUT name, " - please choose what pokemon should fight:" INPUT activePokemonIndex UNTIL activePokemonIndex >= 1 AND activePokemonIndex <= MAX_POKEMON AND pokemon[activePokemonIndex].health > 0 OUTPUT name, " sent in ", pokemon[activePokemonIndex].name OUTPUT "" ENDPROCEDURE PUBLIC PROCEDURE Attack(BYREF enemyPlayer : Player) DECLARE moveChoice : INTEGER OUTPUT "Using: ", pokemon[activePokemonIndex].GetInfo(-1) OUTPUT "" OUTPUT pokemon[activePokemonIndex].GetMoveInfo(TRUE) DECLARE enemyPokemon : Pokemon enemyPokemon ← enemyPlayer.pokemon[enemyPlayer.activePokemonIndex] OUTPUT "ðŸĨŠ ", name, " will attack ", enemyPokemon.GetInfo(-1) OUTPUT "" REPEAT OUTPUT name, " - what move should ", pokemon[activePokemonIndex].name, " do:" INPUT moveChoice IF pokemon[activePokemonIndex].moves[moveChoice].quantity <= 0 THEN OUTPUT "ðŸ”ī All ", pokemon[activePokemonIndex].moves[moveChoice].name, " moves have been used" ENDIF UNTIL moveChoice >= 1 AND moveChoice <= 4 AND pokemon[activePokemonIndex].moves[moveChoice].quantity > 0 OUTPUT "" pokemon[activePokemonIndex].moves[moveChoice].quantity ← pokemon[activePokemonIndex].moves[moveChoice].quantity - 1 DECLARE damage, healthBefore, healthAfter : INTEGER DECLARE fainted : BOOLEAN DECLARE damageMulitplier : REAL damage ← pokemon[activePokemonIndex].moves[moveChoice].damage IF damage > 0 THEN damageMulitplier ← damageModifiers[pokemon[activePokemonIndex].moves[moveChoice].moveType, enemyPokemon.pokemoneType] damage ← INT(damage * damageMulitplier) healthBefore ← enemyPokemon.health healthAfter ← enemyPokemon.TakeDamage(damage) IF healthAfter > 0 THEN OUTPUT "ðŸ—Ąïļ ", pokemon[activePokemonIndex].name, " attacked ", enemyPokemon.name, " - its health fell from ", healthBefore, " to ", healthAfter OUTPUT "" ELSE OUTPUT "💀 ", pokemon[activePokemonIndex].name, " delivered a fatal attack to ", enemyPokemon.name, " - it fainted!" IF enemyPlayer.HasUsablePokemon() = TRUE THEN CALL enemyPlayer.ShowPokemon() CALL enemyPlayer.ChoosePokemon() ELSE gameOver ← TRUE OUTPUT "" OUTPUT "ðŸŠĶ ", enemyPlayer.name, " is out of usable Pokemon - ", name, " has won!!! 🎉" ENDIF ENDIF ELSE healthBefore ← pokemon[activePokemonIndex].health CALL pokemon[activePokemonIndex].Heal(damage * -1) OUTPUT "💉 ", pokemon[activePokemonIndex].name, " was healed from ", healthBefore, " to ", pokemon[activePokemonIndex].health ENDIF ENDPROCEDURE PUBLIC FUNCTION HasUsablePokemon() RETURNS BOOLEAN DECLARE pokemonIndex : INTEGER FOR pokemonIndex ← 1 TO MAX_POKEMON IF pokemon[pokemonIndex].health > 0 THEN RETURN TRUE ENDIF NEXT pokemonIndex RETURN FALSE ENDFUNCTION ENDCLASS CLASS Pokemon PUBLIC name : STRING PUBLIC pokemoneType : TypeRecord PUBLIC health : INTEGER PUBLIC moves : ARRAY[1:4] OF Move PUBLIC PROCEDURE NEW(pName : STRING, pType : TypeRecord, pMoves : ARRAY OF Move) name ← pName pokemoneType ← pType moves ← pMoves health ← 200 ENDPROCEDURE PUBLIC FUNCTION GetInfo(pokemonIndex : INTEGER) RETURNS STRING DECLARE startMsg : STRING startMsg ← "" IF pokemonIndex <> -1 THEN startMsg ← NUM_TO_STR(pokemonIndex) & ") " ENDIF RETURN startMsg & name & " (" & NUM_TO_STR(health) & " health, " & ENUM_TO_STR(pokemoneType) & " type)" ENDFUNCTION PUBLIC FUNCTION GetMoveInfo(showMoveNum : BOOLEAN) RETURNS STRING DECLARE indexNew : INTEGER DECLARE startMsg, msg : STRING msg ← "" FOR indexNew ← 1 TO 4 IF showMoveNum = TRUE THEN startMsg ← " " & NUM_TO_STR(indexNew) & ") " ELSE startMsg ← " - " ENDIF msg ← msg & startMsg & moves[indexNew].name & " (" & NUM_TO_STR(moves[indexNew].damage) & " damage, " & NUM_TO_STR(moves[indexNew].quantity) & " quantity, " & ENUM_TO_STR(moves[indexNew].moveType) & " type)\n" NEXT indexNew RETURN msg ENDFUNCTION PUBLIC FUNCTION TakeDamage(amount : INTEGER) RETURNS INTEGER health ← health - amount RETURN health ENDFUNCTION PUBLIC PROCEDURE Heal(amount : INTEGER) health ← health + amount IF health > 200 THEN health ← 200 ENDIF ENDPROCEDURE ENDCLASS PROCEDURE LoadPokemonFromFile() DECLARE row, col : INTEGER OPENFILE Pokemon.txt FOR READ row ← 1 WHILE NOT EOF(Pokemon.txt) DO FOR col ← 1 TO 6 READFILE Pokemon.txt, allPokemon[row, col] NEXT col row ← row + 1 ENDWHILE CLOSEFILE Pokemon.txt ENDPROCEDURE PROCEDURE LoadMovesFromFile() DECLARE moveIndex : INTEGER DECLARE name, lineTmp : STRING DECLARE moveType : TypeRecord DECLARE damage, quantity : INTEGER OPENFILE PokemonMoves.txt FOR READ moveIndex ← 1 WHILE NOT EOF(PokemonMoves.txt) DO DECLARE moveTmp : Move READFILE PokemonMoves.txt, moveTmp.name READFILE PokemonMoves.txt, lineTmp moveTmp.moveType ← TypeStrToEnum(lineTmp) READFILE PokemonMoves.txt, lineTmp moveTmp.damage ← STR_TO_NUM(lineTmp) READFILE PokemonMoves.txt, lineTmp moveTmp.quantity ← STR_TO_NUM(lineTmp) allMoves[moveIndex] ← moveTmp moveIndex ← moveIndex + 1 ENDWHILE CLOSEFILE PokemonMoves.txt ENDPROCEDURE PROCEDURE DisplayAllStartingPokemon() DECLARE index, moveIndex : INTEGER OUTPUT "=== Available Pokemon ===" OUTPUT "" FOR index ← 1 TO LENGTH(allPokemon) OUTPUT index, ") ", allPokemon[index, 1] FOR moveIndex ← 3 TO 6 OUTPUT " - ", GetMoveInfoStr(allPokemon[index, moveIndex]) NEXT moveIndex OUTPUT "" NEXT index ENDPROCEDURE PROCEDURE EnterNamesAndChoosePokemon() DECLARE playerIndex : INTEGER FOR playerIndex ← 1 TO LENGTH(players) OUTPUT "Player ", playerIndex, " - enter your name:" INPUT tempName players[playerIndex] ← NEW Player(tempName) FOR pokemonIndex ← 1 TO MAX_POKEMON REPEAT OUTPUT "" OUTPUT tempName, " - please choose your Pokemon ", pokemonIndex, " from the list above (1-", ALL_POKEMON, "):" INPUT pokemonChoice UNTIL pokemonChoice >= 1 AND pokemonChoice <= ALL_POKEMON DECLARE tmpMoves : ARRAY[1:4] OF Move DECLARE pokemonMoveIndex, moveIndexInArray : INTEGER FOR pokemonMoveIndex ← 1 TO 4 moveIndexInArray ← GetMoveIndexFromAllMoveArray(allPokemon[pokemonChoice, pokemonMoveIndex + 2]) tmpMoves[pokemonMoveIndex].name ← allMoves[moveIndexInArray].name tmpMoves[pokemonMoveIndex].damage ← allMoves[moveIndexInArray].damage tmpMoves[pokemonMoveIndex].quantity ← allMoves[moveIndexInArray].quantity tmpMoves[pokemonMoveIndex].moveType ← allMoves[moveIndexInArray].moveType NEXT pokemonMoveIndex pokemonTmp ← NEW Pokemon(allPokemon[pokemonChoice, 1], TypeStrToEnum(allPokemon[pokemonChoice, 2]), tmpMoves) players[playerIndex].pokemon[pokemonIndex] ← pokemonTmp OUTPUT "☑ïļ ", players[playerIndex].name, " added ", players[playerIndex].pokemon[pokemonIndex].name, " to their deck!" OUTPUT "" NEXT pokemonIndex NEXT playerIndex ENDPROCEDURE FUNCTION GetMoveInfoStr(moveName : STRING) RETURNS STRING DECLARE index : INTEGER index ← GetMoveIndexFromAllMoveArray(moveName) RETURN allMoves[index].name & " (" & NUM_TO_STR(allMoves[index].damage) & " damage, " & NUM_TO_STR(allMoves[index].quantity) & " quantity, " & ENUM_TO_STR(allMoves[index].moveType) & " type)" ENDFUNCTION FUNCTION GetMoveIndexFromAllMoveArray(moveName : STRING) RETURNS INTEGER DECLARE index : INTEGER DECLARE found : BOOLEAN index ← 0 found ← FALSE WHILE found = FALSE DO index ← index + 1 IF allMoves[index].name = moveName THEN found ← TRUE ENDIF ENDWHILE RETURN index ENDFUNCTION FUNCTION TypeStrToEnum(t : STRING) RETURNS TypeRecord CASE OF t "normal": RETURN TypeRecord.normal "fire": RETURN TypeRecord.fire "water": RETURN TypeRecord.water "grass": RETURN TypeRecord.grass ENDCASE ENDFUNCTION

The format for the following file is:

  • Pokemon name
  • Pokemon type
  • Move 1
  • Move 2
  • Move 3
  • Move 4

As you can see, data for each Pokemon takes up 6 lines - data for each of the 4 moves are in the file example at the bottom of the page

The format for the following file is:

  • Move name
  • Move type
  • Damage
  • Quantity

Note that if a move has negative damage (only "Rest" here), that means that rather than inflicting damage on the opponent, they will instead heal themselves