Question 1: Ceasar Cypher (6 points)¶
Back in the days of the Roman Empire, it was not uncommon for military communications to be intercepted, since they were largely carried around by soldiers on horses. As a result, the Romans developed a way of encoding their military communications, so that they could only be read by other Romans who knew how the message had been encoded.
The code was this: Take every letter of the latin alphabet in the message, and replace it with the letter that was three letters after it in the alphabet. So, A became D, B became E, et cetera. This is known as the Ceasar Cypher, and it is one of the earliest forays humanity ever made into the fascinating science of cryptography.
Write a python function, ceasarian(x), which, given a string x, encodes all characters in the classical latin alphabet (given below), using the Ceasar Cypher described above.
The classical Latin alphabet:
A B C D E F G H I K L M N O P Q R S T V X Y Z
• You do not need to encode characters that do not appear in the classical Latin alphabet, just leave them undeciphered. This includes but is not limited to:
▪ spaces
▪ punctuation
▪ Alphabetic characters not appearing above, such as the modern letter J, which didn’t exist at the time.
• You may assume that Romans shouted everything (i.e., all communications are ENTIRELY IN UPPERCASE)
In addition, give the function written above a default argument, decypher, with a default value of False, which, if True, performs the reverse operation (or decyphering) of encoded text.
In [ ]:
# Your solution here
In [ ]:
# The following test cases are passages from Marcus Aurelius’ Meditations.
a = “TAKE CARE THAT YOU DON’T TREAT INHUMANITY AS IT TREATS HUMAN BEINGS.”
b = “AND WHY SHOULD WE FEEL ANGER AT THE WORLD? AS IF THE WORLD WOULD NOTICE!”
c = “KINGSHIP: TO EARN A BAD REPUTATION BY GOOD DEEDS.”
d = “AMBITION MEANS TYING YOUR WELL-BEING TO WHAT OTHER PEOPLE SAY OR DO.\nSELF-INDULGENCE MEANS TYING IT TO THE THINGS THAT HAPPEN TO YOU\nSANITY MEANS TYING IT TO YOUR OWN ACTIONS.”
a_cypher = “YDNH FDVH YLDY BRU GRQ’Y YVHDY MQLUPDQMYB DX MY YVHDYX LUPDQ EHMQKX.”
b_cypher = “DQG WLB XLRUOG WH IHHO DQKHV DY YLH WRVOG? DX MI YLH WRVOG WRUOG QRYMFH!”
c_cypher = “NMQKXLMS: YR HDVQ D EDG VHSUYDYMRQ EB KRRG GHHGX.”
d_cypher = “DPEMYMRQ PHDQX YBMQK BRUV WHOO-EHMQK YR WLDY RYLHV SHRSOH XDB RV GR.\nXHOI-MQGUOKHQFH PHDQX YBMQK MY YR YLH YLMQKX YLDY LDSSHQ YR BRU\nXDQMYB PHDQX YBMQK MY YR BRUV RWQ DFYMRQX.”
print(ceasarian(a) == a_cypher)
print(ceasarian(b) == b_cypher)
print(ceasarian(c) == c_cypher)
print(ceasarian(d) == d_cypher)
print(ceasarian(a_cypher, True) == a)
print(ceasarian(b_cypher, True) == b)
print(ceasarian(c_cypher, True) == c)
print(ceasarian(d_cypher, True) == d)
In [ ]:
# Hidden Test Case
In [ ]:
# Hidden Test Case
In [ ]:
# Hidden Test Case
Question 2: Just the Questions, Ma’am! (10 points)¶
Write a python function extractQs(filename, sep) which, given the name of a jupyter notebook filename, constructs a new, syntactically correct jupyter notebook which contains only the contents of all markdown cells in the original notebook, in a single markdown cell. The input sep should have a default value of “\n”, and will have functionality described below.
Jupyter notebooks are encoded as json files. If you wish to view the structure of a notebook, select the file in the jupter file browser and click the edit button in the toolbar.
You will only need to modify the cells field of the top-level dictionary. The cells field contains a list of the cells in a notebook. The rest of the metadata should copied over to the new notebook unchanged. A markdown cell (which is a cell containing formatted text, like the one you are currently reading) may be distinguished by the value of the cell_type field, which will be equal to the string “markdown”. A markdown cell will have two other fields, metadata and source. The first of these is an empty dictionary, and the second is a list of strings, where each string is one line of text in the markdown cell.
When creating the new cell containing all the markdown text in the specified file, you must maintain the structure of the original, and give it a list of strings, where each string is a line in the file. Do not change the order in which the markdown text occurs (the first cell should come first, the second comes second, etc.) You must, however, place a separator between the contents of each markdown cell. The string to be used as a separator is specified by the sep input to the function (as seen above). If sep has been set to False, do not add a separator.
Your new notebook should be written as a file with the name Qs_4_
If opening the specified filename fails for any reason, we will assume that the user of the function forgot the file extension. Append .ipynb to the filename and try again. You will also need to append this to the name of the output file if it turns out that the file extension was forgotten. If this fails, create a MalformedQuery exception, without creating the above specified output file.
You must also provide a docstring for this function which describes it’s functionality. This must contain a description of the algorithm, as well as what the algorithm takes as input and produces as output.
In [ ]:
# Your solution here
In [ ]:
#First visible test — the input is SA6.ipynb (6’th assignment)
extractQs(“SA6.ipynb”)
import filecmp
filecmp.cmp(‘SA6_True_Result.ipynb’, ‘Qs_4_SA6.ipynb’)
In [ ]:
#Second visible test — the input is T2.ipynb (Second Test)
extractQs(“T2.ipynb”, “\n\n———————————————–\n\n”)
import filecmp
filecmp.cmp(‘T2_True_Result.ipynb’, ‘Qs_4_T2.ipynb’)
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden Test Case
Question 3: Calendar Alerts (6 points)¶
Write a python function alert(calendar) which, given a calendar dictionary mapping date objects to strings (alerts for the user), outputs the alert text of the nearest alert that will occur within 2 hours of the current time.
Inverse Price is Right rules! We want the nearest alert time without going under the current time. If there are no alerts within the specified time frame, output the string “No alerts presently!”
In [ ]:
# Your solution here
In [ ]:
#First visible test
cal = {
datetime(2020, 3, 3, 5, 0) : “This one is NOT in the next 2 hours range!”,
datetime(2020, 4, 9, 11, 0) : “This one is NOT in the next 2 hours range!”,
datetime(2020, 5, 8, 15, 0) : “This one is NOT in the next 2 hours range!”,
datetime.now() + timedelta(minutes = 30) : “This one should be visible!”,
datetime.now() + timedelta(hours = 1) : “This one is not the first alert in next 2 hours!”,
datetime.now() + timedelta(hours = 1, minutes = 30) : “This one is not the first alert in next 2 hours!”,
datetime(2020, 7, 1, 15, 0) : “This one is NOT in the next 2 hours range!”,
datetime(2020, 8, 10, 15, 0) : “This one is NOT in the next 2 hours range!”,
datetime(2020, 9, 5, 15, 0) : “This one is NOT in the next 2 hours range!”}
alert(cal) == ‘This one should be visible!’
In [ ]:
#Second visible test
cal = {datetime(2020, 6, 8, 9, 30) : “A”, datetime(2020, 7, 8, 21, 0) : “B”, datetime(2020, 8, 8, 14, 30) : “C”}
alert(cal) == ‘No alerts presently!’
In [ ]:
# Hidden test cell
In [ ]:
# Hidden Test Case
In [ ]:
# Hidden test cell
Question 4 : Nick Wars¶
Stick Wars was a popular series of browser games (remember when those were a thing?!) that have recently found a new audience on mobile platforms. You will be coding a stripped-down version of stick wars as a series of classes in python. (This game is very similar to The Battle Cats, incidentally).
Description of the game¶
The student is encouraged to try the game in order to get a feel for it (http://www.stickpage.com/stickwargameplay.shtml), however, please understand that the link is provided on a “do so at your own risk” basis. (The professor disavows any involvment in getting students hooked on browser games during their final exam).
In our version of stick wars (henceforth known as “Nick Wars”), two armies fight each other across a field. Each player has a pool of gold that gradually increases at a fixed rate. Each player may either purchase units to attack the other player, or increase their rate of gold production. The object is to destroy your opponent’s base, and in order to do so, one must destroy the opponent’s army.
In contrast to full version of the game available at the link above, the following simplifications will be made:
• We will not keep track of the position of units in the field. This saves us from complicating their AI with movement behaviour, and saves us from having to make attack range calculations.
• As a result of not tracking unit positions, we also will not worry about being able to instruct units to advance, retreat, or hold position.
• We will not have upgrades, or worry about multiple stages.
• We will not be coding an enemy AI for the player to fight against.
When in doubt, please refer to the descriptions provided in this exam. If there are any conflicts between the operation of the original game and the description provided in this exam, the description in the exam takes precedence.
Question 4a – The Unit Class (8 points)¶
“Units” are the soldiers that fight in our fictitious war. Units will be defined by a class in python called Unit. For Nick Wars, our units will have the following properties:
• Team – A string indicating which team the unit belongs to. This will always either be “Red” or “Blue”. Must be initialized.
• HP – hitpoints / health / energy / pep. The amount of damage a unit can sustain before being killed. Units can not regain or recover HP. Must be initialized.
• ATT – attack / strength. The amount of damage a unit can do in a single hit. Must be initialized.
• attack_timeout – The amount of time that must pass between attacks, measured in tick’s (see methods below). Must be initialized.
• isDead – True if this unit is dead, False if it is living. Initializes to False.
Methods:
• tick(self, opponents) – each time tick is called, we simulate one unit of time passing inside the game scenario. Any “automatic” behaviours of a unit are defined here (so, we are in essence programming the unit AI). The argument opponents is a list of all units which are valid attack targets. The following behaviours must be created if isDead is False. A dead unit performs none of these behaviours and immediately returns the integer value 0.
▪ If the list of opponents has any living units, this unit attacks the unit with the lowest HP (see the attack method). In this case, this method returns the integer value 0. If multiple units have the same HP, go with the first unit with that HP in the list of opposing units.
▪ If the list of opponents is empty or contains no living units, return this unit’s attack value. This will be used later to calculate damage to the enemy’s base.
▪ The above two actions can only occur if it has been the specified number of ticks since the last attack by this unit. How to keep try of this is left as an exercise to the reader.
• attack(self, other) – Called when this unit (self) attacks a defending unit (other). The defending unit has it’s HP reduced by this unit’s attack value. If this reduces the defending unit’s HP to or past zero, set isDead to True.
NOTE: A unit is able to attack (with respect to the timeout) immediately upon creation.
In [ ]:
# Your solution here
In [ ]:
#Visible test
swordsman = Unit(‘Red’, 100, 7, 3)
archer = Unit(‘Blue’, 70, 5, 2)
for i in range(15):
swordsman.tick([archer])
archer.tick([swordsman])
print(‘HP calculation for swordsman: ——‘, swordsman.HP == 75)
print(‘HP calculation for archer: ———‘, archer.HP == 42)
swordsman_base_damage = 0
for i in range(36):
swordsman_base_damage += swordsman.tick([archer])
archer.tick([swordsman])
print(‘Status estimation for swordsman: —‘, swordsman.isDead == False)
print(‘Status estimation for archer: ——‘, archer.isDead == True)
print(‘Base damage calculation: ———–‘, swordsman_base_damage == 21)
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden Test Case
In [ ]:
# Hidden test cell
Question 4b – The Army Class (10 points)¶
Since a lot of code will be reused between the two opposing armies, it makes sense to encapsulate them as their own class.
Define an Army class with the following properties and methods:
Properties:
• Team – A string indicating which team the unit belongs to. This will always either be “Red” or “Blue”. Must be initialized.
• BaseHP – The amount of health the army’s base has remaining. Initializes to 1000
• GoldRate – The number of gold the army gains per tick. Initializes to 10.
• Gold – The amount of gold the team has for buying units and increasing GoldRate. Initializes to 0.
• Roster – A list of dictionaries specifying the properties of the units available for purchase. Must be initialized.
▪ Price – The amount of gold the unit costs
▪ HP – amount of health the unit starts with
▪ ATT – attack power of the unit
▪ attack_timeout – number of time the unit must wait between attacks.
• Units – A list of Unit objects which currently exist.
Methods:
• tick(self,opponents) – Aside from self, takes a list of the other team’s units opponents. Invokes the tick methods of every unit in the list of Units, and passes each the list of opponents. The return values of each Unit’s tick method indicate the amount of damage done to the opponent’s base. Compute the sum of these return values and return that sum from this tick method. In addition, the team’s Gold value is increased by the value of GoldRate.
• BuryDead(self) – Removes from the list of Units all Units for which isDead is True.
• BuyGoldRate(self) – If the team has more than 250 gold, reduces the team’s gold by 250 and adds 2 to GoldRate.
• BuyUnit(self, x) – x is an integer indicating the index of the unit in the roster is to be purchased. If the team has an amount of gold greater than or equal to the Price value of the unit specified by x, a new Unit is initialized with the properties specified in the roster entry, and is added to the Units list. If the specified roster item doesn’t exist, raise a NoSuchUnitError exception. If there is not enough gold to purchase the unit, create a TooPoorError exception.
In [ ]:
# Your code here
In [ ]:
#Visible test
RedArmy = Army(‘Red’, [{‘Price’: 20, ‘HP’: 100, ‘ATT’: 7, ‘attack_timeout’: 3},
{‘Price’: 15, ‘HP’: 70, ‘ATT’: 5, ‘attack_timeout’: 2}])
BlueArmy = Army(‘Blue’, [{‘Price’: 13, ‘HP’: 60, ‘ATT’: 4, ‘attack_timeout’: 1},
{‘Price’: 16, ‘HP’: 80, ‘ATT’: 6, ‘attack_timeout’: 3}])
# Collecting gold
for i in range(30):
RedArmy.tick([])
BlueArmy.tick([])
RedArmy.BuyGoldRate()
BlueArmy.BuyGoldRate()
for i in range(5):
RedArmy.tick([])
BlueArmy.tick([])
# Training the army
RedArmy.BuyUnit(0)
RedArmy.BuyUnit(0)
RedArmy.BuyUnit(1)
BlueArmy.BuyUnit(1)
BlueArmy.BuyUnit(1)
BlueArmy.BuyUnit(1)
BlueArmy.BuyUnit(0)
print(‘Gold calculation for Blue: ——–‘, BlueArmy.Gold == 49)
print(‘Gold calculation for Red: ———‘, RedArmy.Gold == 55)
# Fight
BlueDamage = 0
RedDamage = 0
BlueUnits_HP = []
BlueUnits_Dead = []
RedUnits_HP = []
RedUnits_Dead = []
for i in range(80):
BlueDamage += RedArmy.tick(BlueArmy.Units)
RedDamage += BlueArmy.tick(RedArmy.Units)
for u in BlueArmy.Units:
BlueUnits_HP += [u.HP]
BlueUnits_Dead += [u.isDead]
for u in RedArmy.Units:
RedUnits_HP += [u.HP]
RedUnits_Dead += [u.isDead]
print(‘Damage calcultion for Blue: ——-‘, BlueDamage == 0)
print(‘Damage calcultion for Red: ——–‘, RedDamage == 42)
print(‘Blue units HP: ——————–‘, BlueUnits_HP == [-4, 31, 80, -4] or BlueUnits_HP == [0, 31, 80, 0])
print(‘Blue units status: —————-‘, BlueUnits_Dead == [True, False, False, True])
print(‘Red units HP: ———————‘, RedUnits_HP == [-4, -2, 0] or RedUnits_HP == [0, 0, 0])
print(‘Red units status: —————–‘, RedUnits_Dead == [True, True, True])
In [ ]:
# Hidden Test Case
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
Question 4c – The NickWars Class (6 points)¶
Now we can pull everything together, and define a NickWars class with the following properties and Methods.
Properties:
• Red – An “Army” object, with Team initialized to “Red”. Roster must be initialized.
• Blue – An “Army” object, with Team initialized to “Blue”. Roster must be initialized.
Methods:
• tick(self) – Invokes the tick method for both Red and Blue, supplying each the other’s list of units. The return value of Red’s tick method must be subtracted from the BaseHP of Blue, and vice versa. Every 10 ticks, both Army’s BuryDead methods should be invoked.
• victoryCheck (self) – Returns the winner of the game. If Blue’s BaseHP is zero or less, return “Red Wins!”. If Red’s BaseHP is zero or less, return “Blue Wins!”. If both army’s base hp is zero or less, return “Tie Game!”, and if neither’s base HP is zero or less, return “Still Playing!”
NOTE: Although they didn’t in WWII, the Red Army always moves first in Nick Wars.
In [ ]:
# Your code here
In [ ]:
#Visible test
red_roster = [{‘Price’: 20, ‘HP’: 100, ‘ATT’: 7, ‘attack_timeout’: 3},
{‘Price’: 15, ‘HP’: 70, ‘ATT’: 5, ‘attack_timeout’: 2}]
blue_roster = [{‘Price’: 18, ‘HP’: 60, ‘ATT’: 6, ‘attack_timeout’: 1},
{‘Price’: 16, ‘HP’: 80, ‘ATT’: 6, ‘attack_timeout’: 3}]
NW = NickWars(red_roster, blue_roster)
game_status = “Still Playing!”
# Collecting gold
for i in range(20):
NW.tick()
# Training troops
NW.Red.BuyUnit(0)
NW.Red.BuyUnit(1)
NW.Red.BuyUnit(1)
NW.Red.BuyUnit(1)
NW.Red.BuyUnit(1)
NW.Blue.BuyUnit(0)
NW.Blue.BuyUnit(0)
NW.Blue.BuyUnit(0)
NW.Blue.BuyUnit(1)
NW.Blue.BuyUnit(1)
# Fight
while (game_status == “Still Playing!”):
NW.tick()
BlueUnits_HP = []
for u in NW.Blue.Units:
BlueUnits_HP += [u.HP]
RedUnits_HP = []
for u in NW.Red.Units:
RedUnits_HP += [u.HP]
#print(BlueUnits_HP)
#print(RedUnits_HP)
game_status = NW.victoryCheck()
# Print the result
BlueUnits_HP = []
for u in NW.Blue.Units:
BlueUnits_HP += [u.HP]
print (‘Final Base HP test for Blue: —- ‘, NW.Blue.BaseHP == 1000)
print (‘Final Base HP test for Red: —- ‘, NW.Red.BaseHP == -2 or NW.Red.BaseHP == 0)
print (‘Final HP test for Blue: —- ‘, BlueUnits_HP == [52])
print (game_status) # Blue would win…
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
BONUS QUESTION: Hackers don’t die, they respawn AKA are you a bad enough dude to save the president? (6 points)¶
The President of the Country has been captured by aliens! It’s up to you, Hacker McHackerson, part of the government’s elite anti-alien hacking unit, to determine the location of the President so that the military can send in the troops and save the President!
Fortunately, the aliens never took COMPSCI 1MD3, and have left their database of kidnapping victims vulnerable to SQL injection attacks! The source code for a python function the aliens wrote to query their database has been delivered to HQ. Many Bothans died to bring us this information.
In [ ]:
# Source code for the alien’s very hackable database querying program.
### SPECIAL MESSAGE FROM EARTH HEADQUARTERS ###
#
# Our intelligence indicates the aliens wrote this script because they were frustrated that you can’t
# get any select query results using executescript. So, they wanted to reproduce the functionality of
# executescript in a way that allows the user to see all the return results of a set of queries.
#
# Another alien, unaware of the vulnerability this creates, then wrote another function to autoformat an
# insert query.
#
# Unfortunately, these functions are still mostly written in Alien.
#
###############################################
import sqlite3
def blorksnork(xorkThorb) :
snerk = []
borkbork = sqlite3.connect(‘MorkDork.db’)
glorb = borkbork.cursor()
borknork = glorb.execute(xorkThorb)
for glork in borknork :
snerk += [str(glork)]
borkbork.commit()
borkbork.close()
return snerk
def gorkaplorkplork (shmork) :
flerb = []
xork = ‘INSERT INTO prisoners (name, lattitude, longitude, resistance) VALUES (“{0}”, {1}, {2}, “{3}”)’.format(shmork[0], shmork[1], shmork[2], shmork[3])
xorkTork = xork.split(“;”)
for lork in xorkTork :
flerb += blorksnork(lork)
return flerb
You must create SQL code injections that produce the following
1. Display the entire prisoners table
2. Display just the lattitude and longitude of the President (HINT: The President’s name is “The President”)
3. Design an attack query that destroys the alien’s prisoners table!
In [ ]:
# If you mess up the database while experimenting on the above, the following code will regenerate it.
# Run this cell to regenerate and restore the database.
import sqlite3
def regenDatabase () :
db = sqlite3.connect(‘MorkDork.db’)
cur = db.cursor()
try :
cur.execute(“drop table prisoners;”)
except :
pass
cur.execute(“create table prisoners (name TEXT, lattitude REAL, longitude REAL, resistance TEXT);”)
cur.execute(‘insert into prisoners values (“Michael Scarn”, 51.1789, 1.8262, “Low”)’)
cur.execute(‘insert into prisoners values (“The President”, 37.2343, -115.8067, “High”)’)
cur.execute(‘insert into prisoners values (“Elvis Presley”, 35.054863, -90.026722, “Very High”)’)
db.commit()
db.close()
def checkDataBase () :
db = sqlite3.connect(‘MorkDork.db’)
cur = db.cursor()
cur.execute(“select * from prisoners;”)
for row in cur :
print(row)
regenDatabase()
checkDataBase ()
# I know this kind of breaks character with respect to this question,
# but it’s better than not having the ability to regenerate the database, believe me.
In [ ]:
# Your solution here
def attack1 () :
a = ‘your code here’
b = ‘your code here’
c = ‘your code here’
d = ‘your code here’
return (a,b,c,d)
def attack2 () :
a = ‘your code here’
b = ‘your code here’
c = ‘your code here’
d = ‘your code here’
return (a,b,c,d)
def attack3 () :
a = ‘your code here’
b = ‘your code here’
c = ‘your code here’
d = ‘your code here’
return (a,b,c,d)
In [ ]:
#Visible test
regenDatabase()
print(“Test 1:”)
print(gorkaplorkplork(attack1()))
print(“—————–“)
print(“Test 2:”)
print(gorkaplorkplork(attack2()))
print(“—————–“)
print(“Test 3:”)
print(gorkaplorkplork(attack3()))
try :
checkDataBase ()
except :
print(“Success!”)
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell
In [ ]:
# Hidden test cell