diff --git a/database.swift b/database.swift new file mode 100644 index 0000000..2425a2f --- /dev/null +++ b/database.swift @@ -0,0 +1,31 @@ +public class SetOfArrays { + + private var stopwordsArray = ["i", "me", "my", "myself", "we", "our", "ours", "ourselves", "you", "your", "yours", "yourself", "yourselves", "he", "him", "his", "himself", "she", "her", "hers", "herself", "it", "its", "itself", "they", "them", "their", "theirs", "themselves", "what", "which", "who", "whom", "this", "that", "these", "those", "am", "is", "are", "was", "were", "be", "been", "being", "have", "has", "had", "having", "do", "does", "did", "doing", "a", "an", "the", "and", "but", "if", "or", "because", "as", "until", "while", "of", "at", "by", "for", "with", "about", "against", "between", "into", "through", "during", "before", "after", "above", "below", "to", "from", "up", "down", "in", "out", "on", "off", "over", "under", "again", "further", "then", "once", "here", "there", "when", "where", "why", "how", "all", "any", "both", "each", "few", "more", "most", "other", "some", "such", "only", "own", "same", "so", "than", "too", "very", "can", "will", "just", "should", "now", "want", ".", ","] + + private var lemmaMatrix = [["go", "move", "went", "gone", "step", "walk", "chose"], + ["axe", "axe", "axr", "aze", "ace", "weapon"], + ["gun", "gin", "rifle", "shotgun"], + ["take", "grab", "snatch", "yoink", "yes", "yeah", "yep"], + ["no", "don't", "dont", "nope", "nay", "nah"], + ["fight", "shoot", "use", "swing"]] + + func getStopwords() -> [String] { + return stopwordsArray + } + + func getLemmaMatrix() -> [[String]] { + return lemmaMatrix + } + + func getGoArray() -> [String] { + return lemmaMatrix[0] + } + + func getSwordArray() -> [String] { + return lemmaMatrix[1] + } + + func getGunArray() -> [String] { + return lemmaMatrix[2] + } +} diff --git a/levelOne.swift b/levelOne.swift new file mode 100644 index 0000000..87a725a --- /dev/null +++ b/levelOne.swift @@ -0,0 +1,206 @@ +import Curses + +class LevelOne { + + private var answer1 = "" + private var answer2 = "" + private var answer3 = "" + private var movement = false + private var house = false + private var car = false + private var take = false + + func storyTellingOne() { + /////////////////////////////////////////// + // Tells the story and current situation // + /////////////////////////////////////////// + currentCursorY = 0 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("You awake in a cold, cold place. You dont know where you are, or how you got here. All you know is your name: \(name).") + + currentCursorY += 1 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("You see a path near where you wake up, it splits into two up ahead.") + + currentCursorY += 1 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("Your two choices are the left path and the right path. The left path leads to the house and the right path leads to the car.") + + currentCursorY += 1 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("What do you do?") + + // Waits for user response + currentCursorY = 10 + answer1 = mainWindow.getStringFromTextField(at:Point(x:0, y:currentCursorY), maxCharacters: 100, fieldColorPair:whiteOnBlack) + + // Processes the response from the user + let stringArray = answer1.components(separatedBy: " ") + + let filteredTokens = NLP().stemming(stringArray: stringArray) + + let lemmatizedTokens = NLP().lemmatization(stringArray: filteredTokens) + + // Uses the left over array and looks for keywords + for word in lemmatizedTokens { + switch word { + case "go": + movement = true + case "house", "left": + house = !house + break + case "car", "right": + car = !car + break + case "no": + car = !car + house = !house + + default: + currentCursorY = 9 + mainWindow.write("Please enter a valid response") + } + } + + // Using the response, sends the user to the next section + if movement && car { + storyTellingCar() + } else if movement && house { + storyTellingHouse() + } + } + + + func storyTellingCar() { + ///////////////////// + // Tells the story // + ///////////////////// + mainWindow.clear() + currentCursorY = 1 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + + mainWindow.write("You walk to the car to look for anything that can help you.") + currentCursorY = 2 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + + mainWindow.write("In the trunk of the car, you see a woodcutting axe. This could be useful to you.") + currentCursorY = 3 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + + mainWindow.write("Do you take the axe?") + currentCursorY = 4 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + + // Waits for new user response + currentCursorY = 10 + answer2 = mainWindow.getStringFromTextField(at:Point(x:0, y:currentCursorY), maxCharacters: 100, fieldColorPair:whiteOnBlack) + + // + // Again filteres the response from the user + // + let stringArray = answer2.components(separatedBy: " ") + + let filteredTokens = NLP().stemming(stringArray: stringArray) + + let lemmatizedTokens = NLP().lemmatization(stringArray: filteredTokens) + + for word in lemmatizedTokens { + switch word { + case "take": + take = !take + weapon = !weapon + case "axe": + weapon = !weapon + case "no": + take = !take + weapon = !weapon + + break + default: + currentCursorY = 9 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("Please enter a valid response") + } + } + if take && weapon { + inventory[0] = "axe" + currentCursorY = 11 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("You chose to take the axe, good choice \(name).") + } else { + currentCursorY = 11 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("You chose to leave the axe, you might regret that decision \(name).") + } + currentCursorY = 12 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("Press F2 to proceed to the next level") + } + + func storyTellingHouse() { + mainWindow.clear() + currentCursorY = 1 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("You chose to go to the house to see if there is anything you can find.") + + currentCursorY = 2 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + + mainWindow.write("You look around the house and find a safe that is slightly open, which has a gun in it.") + + currentCursorY = 3 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + + mainWindow.write("Do you take the gun?") + + currentCursorY = 10 + + answer3 = mainWindow.getStringFromTextField(at:Point(x:0, y:currentCursorY), maxCharacters: 100, fieldColorPair:whiteOnBlack) + + let stringArray = answer3.components(separatedBy: " ") + + let filteredTokens = NLP().stemming(stringArray: stringArray) + + let lemmatizedTokens = NLP().lemmatization(stringArray: filteredTokens) + + for word in lemmatizedTokens { + switch word { + case "take": + take = !take + weapon = !weapon + case "gun": + weapon = !weapon + case "no": + take = !take + weapon = !weapon + break + default: + currentCursorY = 9 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("Please enter a valid response") + } + } + + if take && weapon { + inventory[0] = "gun" + currentCursorY = 11 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("You chose to take the gun, good choice \(name).") + } else { + currentCursorY = 11 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("You chose to leave the gun, you might regret that decision \(name).") + } + currentCursorY = 12 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("Press F2 to proceed to the next level") + } + + func isWeaponObtained() -> Bool { + return weapon + } + + func getInv() -> [String] { + return inventory + } +} diff --git a/levelTwo.swift b/levelTwo.swift new file mode 100644 index 0000000..f806f7e --- /dev/null +++ b/levelTwo.swift @@ -0,0 +1,86 @@ +import Curses + +class LevelTwo { + + private var battleAnswer = "" + private var fight = false + private var flight = false + private var damage = 0 + + func getRandomRunStory() -> String { + let runStories = ["You try to run but trip and the bear kills you.", "You try to run but the bear is faster than you.", "You try to run but fall and die."] + let randomNum = Int.random(in: 0 ... 2) + + return runStories[randomNum] + } + + func battleBear() { + while true { + mainWindow.clear() + mainWindow.write("While searching you hear a sound nearby.") + currentCursorY = 1 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("Its a wild bear that is about to attack you.") + + currentCursorY = 2 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write("What do you do?") + + currentCursorY = 10 + battleAnswer = mainWindow.getStringFromTextField(at:Point(x:0, y:currentCursorY), maxCharacters: 100, fieldColorPair:whiteOnBlack) + + let stringArray = battleAnswer.components(separatedBy: " ") + + let filteredTokens = NLP().stemming(stringArray: stringArray) + + let lemmatizedTokens = NLP().lemmatization(stringArray: filteredTokens) + + for word in lemmatizedTokens { + switch word { + case "run": + flight = true + case "fight": + fight = true + + case "inventory": + currentCursorY = 8 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + print(inventory) + default: + currentCursorY = 9 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + } + } + + switch inventory[0] { + case "gun": + damage = Int.random(in: 15 ... 20) + case "axe": + damage = Int.random(in: 10 ... 20) + default: + damage = 0 + } + + + if fight { + currentCursorY = 8 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + if LevelOne().getInv()[0] == "gun" && damage >= 17 { + mainWindow.write("You shot the bear and survived") + } else if LevelOne().getInv()[0] == "axe" && damage >= 16 { + mainWindow.write("You fought the bear with your axe and survived") + } else if LevelOne().isWeaponObtained() && damage < 16 { + mainWindow.write("You were not able to kill the bear. Try again") + } else { + mainWindow.write("You didnt have a weapon, so you couldnt defend yourself against the bear.") + } + break + } else { + currentCursorY = 10 + mainWindow.cursor.position = Point(x: 0, y: currentCursorY) + mainWindow.write(getRandomRunStory()) + break + } + } + } +} diff --git a/main.swift b/main.swift new file mode 100644 index 0000000..e6332a6 --- /dev/null +++ b/main.swift @@ -0,0 +1,91 @@ +import Foundation +import Curses + +// Define interrupt handler to terminate Curses with CTRL-C +class Handler : CursesHandlerProtocol { + func interruptHandler() { + screen.shutDown() + print("Good bye!") + exit(0) + } + + func windowChangedHandler() { + } +} + +// Start up Curses +let screen = Screen.shared +screen.startUp(handler:Handler()) + +// Insert your code +let keyboard = Keyboard.shared + +let colors = Colors.shared +precondition(colors.areSupported, "This terminal doesn't support colors") +colors.startUp() + +let mainWindow = screen.window + +let white = Color.standard(.white) +let black = Color.standard(.black) +let whiteOnBlack = colors.newPair(foreground:black, background:white) + +var titleRow1 = "ooooooooooo oooo ooooooooooo oooo" +var titleRow2 = "88 888 88 888ooooo ooooooooo8 88 888 88 oooo oooo oo oooooo ooooo888 oo oooooo ooooooo " +var titleRow3 = " 888 888 888 888oooooo8 888 888 888 888 888 888 888 888 888 ooooo888 " +var titleRow4 = " 888 888 888 888 888 888 888 888 888 888 888 888 888 888 " +var titleRow5 = " o888o o888o o888o 88oooo888 o888o 888o88 8o o888o o888o 88ooo888o o888o 88ooo88 8o " + + +var name = "" +var currentCursorY = 0 +var inventory = [""] +var weapon = false + +// Prints the startup message +mainWindow.clear() +mainWindow.cursor.position = Point(x: 65, y: 8) +mainWindow.write("Welcome to") +mainWindow.cursor.position = Point(x: 20, y: 11) +mainWindow.write(titleRow1) +mainWindow.cursor.position = Point(x: 20, y: 12) +mainWindow.write(titleRow2) +mainWindow.cursor.position = Point(x: 20, y: 13) +mainWindow.write(titleRow3) +mainWindow.cursor.position = Point(x: 20, y: 14) +mainWindow.write(titleRow4) +mainWindow.cursor.position = Point(x: 20, y: 15) +mainWindow.write(titleRow5) +mainWindow.cursor.position = Point(x: 60, y: 20) +mainWindow.write("Type your name below:") +name = mainWindow.getStringFromTextField(at:Point(x:65, y:22), maxCharacters: 20, fieldColorPair:whiteOnBlack) +mainWindow.cursor.position = Point(x: 0, y: currentCursorY + 2) +mainWindow.write("Press F<#> to start the corresponding level") + +// Level selector +while true { + let key = keyboard.getKey(window:mainWindow) + + mainWindow.turnOn(Attribute.normal) + + switch(key.keyType) { + case .function1: + mainWindow.clear() + LevelOne().storyTellingOne() + case .function2: + mainWindow.clear() + LevelTwo().battleBear() + default: + mainWindow.write("Waiting for input") + } + + mainWindow.refresh() + if key.keyType == .function4 { + mainWindow.write("Stopping function") + break + } + +} + +// Wait forever, or until the user presses CTRL-C +screen.wait() diff --git a/nlp.swift b/nlp.swift new file mode 100644 index 0000000..8edeee9 --- /dev/null +++ b/nlp.swift @@ -0,0 +1,42 @@ +import Curses + +public class NLP { + + var filteredTokens : [String] = [] + + var lemmatizedTokens : [String] = [] + + // Stems the array to remove any stopwords and suffixes + func stemming(stringArray: [String]) -> [String] { + + for word in stringArray { + var newWord = word.lowercased() + + if !(SetOfArrays().getStopwords().contains(newWord)) { + if newWord.length > 5 && (newWord.suffix(3) == "ing" || newWord.suffix(3) == "ion") { + newWord = String(newWord.prefix(newWord.length - 3)) + } else if newWord.length > 2 && newWord.suffix(2) == "ed" { + newWord = String(newWord.prefix(newWord.length - 2)) + } + filteredTokens.append(newWord) + } + } + return filteredTokens + } + + // Lemmatizes the array to get the root meaning of the words + func lemmatization(stringArray: [String]) -> [String] { + + for word in stringArray { + var newWord = word + + for i in 0 ... SetOfArrays().getLemmaMatrix().count - 1 { + if SetOfArrays().getLemmaMatrix()[i].contains(newWord) { + newWord = SetOfArrays().getLemmaMatrix()[i][0] + } + } + lemmatizedTokens.append(newWord) + } + return lemmatizedTokens + } +}