diff --git a/harm/cli.py b/harm/cli.py new file mode 100644 index 0000000..1b6637e --- /dev/null +++ b/harm/cli.py @@ -0,0 +1,222 @@ +from memory_Context import * +from llm import * + +# Exit +# +# Conditions: +# 1. `e`, `exit`, `q`, or `quit` was entered +# 2. Victory has just been reported +@llm_by_value +def cli_exit( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "input" and + ( + c.input == "e" or + c.input == "exit" or + c.input == "q" or + c.input == "quit" + ) + ): + c.exit = True + c.recentField = "exit" + return c + #} + if ( + c.recentField == "outputVictory" + ): + c.exit = True + c.recentField = "exit" + return c + #} + c.recentField = "none" + return c +#} + +# Ask user to go on +# +# Conditions: +# 1. Items have just matched and there are still items left to select +@llm_by_value +def cli_goOn( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "outputMatchedItems" and + len(c.hiddenItems) != len(c.playfieldItems) + ): + c.outputGoOn = "Go on:" + c.recentField = "outputGoOn" + return c + #} + c.recentField = "none" + return c +#} + +# Greet the user +# +# Conditions: +# 1. Just launched +@llm_by_value +def cli_greetUser( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "didLaunch" and + c.didLaunch == True + ): + c.outputGreeting = "OGS Memory Command Line Interface" + c.recentField = "outputGreeting" + return c + #} + c.recentField = "none" + return c +#} + +# Ask user to select second item to have a pair of selected items +# +# Conditions: +# 1. Single item has just been selected +@llm_by_value +def cli_promptSecondItemSelection( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "selectedItems" and + len(c.selectedItems) == 1 + ): + c.outputPromptSelection = "Select the second item now:" + c.recentField = "outputPromptSelection" + return c + #} + c.recentField = "none" + return c +#} + +# Report matched items +# +# Conditions: +# 1. Items were hidden (i.e., they matched) +@llm_by_value +def cli_reportMatchedItems( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "hiddenItems" + ): + c.outputMatchedItems = "Items matched!" + c.recentField = "outputMatchedItems" + return c + #} + c.recentField = "none" + return c +#} + +# Report victory +@llm_by_value +def cli_reportVictory( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "victory" + ): + c.outputVictory = "VICTORY! The game is over now" + c.recentField = "outputVictory" + return c + #} + c.recentField = "none" + return c +#} + +# Select item +# +# Conditions: +# 1. Id is digit +@llm_by_value +def cli_selectItem( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "input" and + llm_isDigit(c.input) + ): + # CLI ids start with 1 while memory module has ids starting with 0 + # Convert CLI id to memory id + c.selectedId = llm_strToInt(c.input) - 1 + c.recentField = "selectedId" + return c + #} + c.recentField = "none" + return c +#} + +# Show help (aka commands) +# +# Conditions: +# 1. Just launched +# 1. `h` or `help` was entered +@llm_by_value +def cli_showHelp( + c: memory_Context +) -> memory_Context: + if ( + ( + c.recentField == "didLaunch" and + c.didLaunch == True + ) or + ( + c.recentField == "input" and + c.input == "h" + ) or + ( + c.recentField == "input" and + c.input == "help" + ) + ): + c.outputHelp = "Commands:\n\te, exit, q, quit\n\t\tExit\n\th, help\n\t\tList commands\n\t1, 2, 3, ...\n\t\tSelect item\nEnter your choice:" + c.recentField = "outputHelp" + return c + #} + c.recentField = "none" + return c +#} + +# Report mismatched items +@llm_by_value +def cli_reportMismatchedItems( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "mismatchedItems" + ): + c.outputMismatchedItems = "Wrong! Try again:" + c.recentField = "outputMismatchedItems" + return c + #} + c.recentField = "none" + return c +#} + +## Report selection of invalid item ids +## +## Conditions: +## 1. Index out of bounds: less than minimum +## 2. Index out of bounds: greater than maximum +## 3. Item is already hidden +##@llm_by_value +##def cli_shouldReportInvalidItemSelection( +## c: cli_Context +##) -> cli_Context: +## if ( +## c.recentField == "cMemory" and +## c.cMemory.recentField == "selectedItems" and +## len(c.cMemory.selectedItems) == 1 +## ): +## c.outputPromptSelection = "Select the second item now:" +## c.recentField = "outputPromptSelection" +## return c +## #} +## c.recentField = "none" +## return c +###} diff --git a/harm/cli_test.py b/harm/cli_test.py new file mode 100644 index 0000000..9aaafdc --- /dev/null +++ b/harm/cli_test.py @@ -0,0 +1,303 @@ +from cli import * +from memory import * +from memory_Context import * + +def cli_test_exit_e( +) -> str: + c = memory_createContext() + c.input = "e" + c.recentField = "input" + c = cli_exit(c) + if ( + c.recentField == "exit" + ): + return "OK: cli_exit_e" + #} + return "ERR: cli_exit_e" +#} + +def cli_test_exit_exit( +) -> str: + c = memory_createContext() + c.input = "exit" + c.recentField = "input" + c = cli_exit(c) + if ( + c.recentField == "exit" + ): + return "OK: cli_exit_exit" + #} + return "ERR: cli_exit_e" +#} + +def cli_test_exit_victory( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Match the first pair of tiles. + c.input = "1" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c.input = "2" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c = memory_hideMatchingItems(c) + + # Match the second pair of tiles. + c.input = "3" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c.input = "4" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c = memory_hideMatchingItems(c) + c = memory_detectVictory(c) + c = cli_reportVictory(c) + c = cli_exit(c) + + if ( + c.recentField == "exit" + ): + return "OK: cli_exit_victory" + #} + return "ERR: cli_exit_victory" +#} + +def cli_test_exit_q( +) -> str: + c = memory_createContext() + c.input = "q" + c.recentField = "input" + c = cli_exit(c) + if ( + c.recentField == "exit" + ): + return "OK: cli_exit_q" + #} + return "ERR: cli_exit_q" +#} + +def cli_test_exit_quit( +) -> str: + c = memory_createContext() + c.input = "quit" + c.recentField = "input" + c = cli_exit(c) + if ( + c.recentField == "exit" + ): + return "OK: cli_exit_quit" + #} + return "ERR: cli_exit_quit" +#} + +def cli_test_goOn( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Match the first pair of items. + c.input = "1" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c.input = "2" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c = memory_hideMatchingItems(c) + c = cli_reportMatchedItems(c) + + c = cli_goOn(c) + if ( + c.recentField == "outputGoOn" + ): + return "OK: cli_goOn" + #} + return "ERR: cli_goOn" +#} + +def cli_test_greetUser( +) -> str: + c = memory_createContext() + c.didLaunch = True + c.recentField = "didLaunch" + c = cli_greetUser(c) + if ( + c.recentField == "outputGreeting" + ): + return "OK: cli_greetUser" + #} + return "ERR: cli_greetUser" +#} + +def cli_test_promptSecondItemSelection( +) -> str: + c = memory_createContext() + c.input = "1" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c = cli_promptSecondItemSelection(c) + if ( + c.recentField == "outputPromptSelection" + ): + return "OK: cli_promptSecondItemSelection" + #} + return "ERR: cli_promptSecondItemSelection" +#} + +def cli_test_reportMatchedItems( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + c.input = "1" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c.input = "2" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c = memory_hideMatchingItems(c) + c = cli_reportMatchedItems(c) + if ( + c.recentField == "outputMatchedItems" + ): + return "OK: cli_reportMatchedItems" + #} + return "ERR: cli_reportMatchedItems" +#} + +def cli_test_reportMismatchedItems( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + c.input = "1" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c.input = "3" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c = memory_detectMismatchedItems(c) + c = cli_reportMismatchedItems(c) + if ( + c.recentField == "outputMismatchedItems" + ): + return "OK: cli_reportMismatchedItems" + #} + return "ERR: cli_reportMismatchedItems" +#} + +def cli_test_selectItem( +) -> str: + c = memory_createContext() + c.input = "1" + c.recentField = "input" + c = cli_selectItem(c) + if ( + c.recentField == "selectedId" and + c.selectedId == 0 + ): + return "OK: cli_selectItem" + #} + return "ERR: cli_selectItem" +#} + +def cli_test_showHelp_h( +) -> str: + c = memory_createContext() + c.input = "h" + c.recentField = "input" + c = cli_showHelp(c) + if ( + c.recentField == "outputHelp" + ): + return "OK: cli_showHelp_h" + #} + return "ERR: cli_showHelp_h" +#} + +def cli_test_showHelp_help( +) -> str: + c = memory_createContext() + c.input = "help" + c.recentField = "input" + c = cli_showHelp(c) + if ( + c.recentField == "outputHelp" + ): + return "OK: cli_showHelp_help" + #} + return "ERR: cli_showHelp_help" +#} + +def cli_test_reportVictory( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Match the first pair of tiles. + c.input = "1" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c.input = "2" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c = memory_hideMatchingItems(c) + + # Match the second pair of tiles. + c.input = "3" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c.input = "4" + c.recentField = "input" + c = cli_selectItem(c) + c = memory_selectItem(c) + c = memory_hideMatchingItems(c) + c = memory_detectVictory(c) + c = cli_reportVictory(c) + + if ( + c.recentField == "outputVictory" + ): + return "OK: cli_reportVictory" + #} + return "ERR: cli_reportVictory" +#} + +#def cli_test_shouldReportIvalidItemSelection_outOfBoundsMin( +#) -> str: +# c = cli_createContext() +# c.cMemory = memory_createContext() +# c.input = "0" +# c = cli_selectItem(c) +# c = cli_shouldReportInvalidItemSelection(c) +# if ( +# c.recentField == "outputInvalidItemSelection" +# ): +# return "OK: cli_shouldReportInvalidItemSelection" +# #} +# return "ERR: cli_shouldReportInvalidItemSelection" +##} +# diff --git a/harm/ctx.py b/harm/ctx.py new file mode 100644 index 0000000..af8f36e --- /dev/null +++ b/harm/ctx.py @@ -0,0 +1,53 @@ +import copy + +class ctx_Controller: + def __init__(self, c): + self.callbacks = [] + self.context = c + self.functions = [] + self.isProcessingQueue = False + self.queue = [] + + def executeFunctions(self): + c = self.queue.pop(0) + for f in self.functions: + ctx = f(c) + if ctx.recentField != "none": + self.queue.append(ctx) + + self.context.recentField = c.recentField + self.context.setField(c.recentField, c.field(c.recentField)) + self.reportContext() + + def processQueue(self): + # Decline recursion. + if self.isProcessingQueue: + return + self.isProcessingQueue = True + while len(self.queue) > 0: + self.executeFunctions() + self.isProcessingQueue = False + + def registerCallback(self, cb): + self.callbacks.append(cb) + + def registerFieldCallback(self, fieldName, cb): + self.callbacks.append(lambda c: cb(c) if c.recentField == fieldName else None) + + def registerFunction(self, f): + self.functions.append(f) + + def registerFunctions(self, funcs): + for f in funcs: + self.functions.append(f) + + def reportContext(self): + for cb in self.callbacks: + cb(self.context) + + def set(self, fieldName, value): + c = copy.deepcopy(self.context) + c.setField(fieldName, value) + c.recentField = fieldName + self.queue.append(c) + self.processQueue() diff --git a/harm/ctx_test2.py b/harm/ctx_test2.py new file mode 100644 index 0000000..74720c8 --- /dev/null +++ b/harm/ctx_test2.py @@ -0,0 +1,142 @@ +from ctx import * +from memory import * +from memory_Context import * + +def ctx_test_Controller_executeFunctions_registerFunction_set( +) -> str: + c = memory_createContext() + ctrl = ctx_Controller(c) + # Disable automatic invocation of executeFunctions. + ctrl.isProcessingQueue = True + ctrl.set("input", "123") + + @llm_by_value + def processInput(c): + if ( + c.recentField == "input" + ): + c.outputHelp = "Checked" + c.recentField = "outputHelp" + return c + #} + c.recentField = "none" + return c + #} + + ctrl.registerFunction(processInput) + # Apply 'input'. + ctrl.executeFunctions() + # Apply 'outputHelp'. + ctrl.executeFunctions() + if ( + c.input == "123" and + c.outputHelp == "Checked" + ): + return "OK: ctx_Controller_executeFunctions_set" + #} + return "ERR: ctx_Controller_executeFunctions_set" +#} + +def ctx_test_Controller_processQueue( +) -> str: + c = memory_createContext() + ctrl = ctx_Controller(c) + + @llm_by_value + def processInput(c): + if ( + c.recentField == "input" + ): + c.outputHelp = "Checked" + c.recentField = "outputHelp" + return c + #} + c.recentField = "none" + return c + #} + + ctrl.registerFunction(processInput) + ctrl.set("input", "abc"); + if ( + c.input == "abc" and + c.outputHelp == "Checked" + ): + return "OK: ctx_Controller_processQueue" + #} + return "ERR: ctx_Controller_processQueue" +#} + +def ctx_test_Controller_registerFieldCallback_match( +) -> str: + c = memory_createContext() + ctrl = ctx_Controller(c) + c.input = "123" + c.recentField = "input" + + globals()["callbackInput"] = "" + def setCallbackInput(c): + if ( + c.recentField == "input" + ): + globals()["callbackInput"] = c.input + + ctrl.registerFieldCallback("input", setCallbackInput) + ctrl.reportContext() + if ( + c.input == globals()["callbackInput"] + ): + return "OK: ctx_Controller_registerFieldCallback_match" + #} + return "ERR: ctx_Controller_registerFieldCallback_match" +#} + +def ctx_test_Controller_registerFieldCallback_mismatch( +) -> str: + c = memory_createContext() + ctrl = ctx_Controller(c) + c.input = "123" + c.outputHelp = "you" + # A field other than 'input' is marked recent. + c.recentField = "outputHelp" + + globals()["callbackInput"] = "" + def setCallbackInput(c): + if ( + c.recentField == "input" + ): + globals()["callbackInput"] = c.input + + ctrl.registerFieldCallback("input", setCallbackInput) + ctrl.reportContext() + if ( + globals()["callbackInput"] == "" + ): + return "OK: ctx_Controller_registerFieldCallback_mismatch" + #} + return "ERR: ctx_Controller_registerFieldCallback_mismatch" +#} + +def ctx_test_memoryContext_field( +) -> str: + c = memory_createContext() + c.input = "abc" + if ( + c.field("input") == "abc" + ): + return "OK: ctx_memoryContext_field" + #} + return "ERR: ctx_memoryContext_field" +#} + +def ctx_test_memoryContext_setField( +) -> str: + c = memory_createContext() + c.input = "abc" + c.setField("input", "123") + if ( + c.input == "123" + ): + return "OK: ctx_memoryContext_setField" + #} + return "ERR: ctx_memoryContext_setField" +#} diff --git a/harm/desktop.py b/harm/desktop.py new file mode 100644 index 0000000..98ee3f5 --- /dev/null +++ b/harm/desktop.py @@ -0,0 +1,317 @@ +import arcade +from gui_aux import * +from desktop_Platform import * + +def desktop_createDesc(p): + p.desc = arcade.Sprite() + p.descSprites.append(p.desc) + p.desc.texture = p.descTextures[0] + # Position. + pos = gui_aux_cellScreenPosition(p.c, p.c.descPosition) + p.desc.left = pos[0] + p.desc.top = pos[1] + # Invisible by default. + p.desc.visible = False +#} + +def desktop_createDeselectedTiles(p): + for (id, pos) in enumerate(p.c.tilePositions): + tile = arcade.AnimatedTimeBasedSprite() + p.deselectedTiles.append(tile) + p.deselectedSprites.append(tile) + + tile.guid = id + tile.texture = p.textures[0] + # Animation between two textures. + a1 = arcade.sprite.AnimationKeyframe(0, 700, p.textures[0]) + a2 = arcade.sprite.AnimationKeyframe(1, 700, p.textures[1]) + tile.frames.append(a1) + tile.frames.append(a2) + # Position. + tile.left = pos[0] + tile.top = pos[1] + #} +#} + +def desktop_createSelectedTiles(p): + for (id, pos) in enumerate(p.c.tilePositions): + tile = arcade.Sprite() + p.selectedTiles.append(tile) + p.selectedSprites.append(tile) + + tile.guid = id + tile.texture = p.textures[2 + p.c.playfieldItems[id]] + # Position. + tile.left = pos[0] + tile.top = pos[1] + # Invisible by default. + tile.visible = False + #} +#} + +def desktop_createSplash(p): + p.splash = arcade.Sprite() + p.splashSprites.append(p.splash) + p.splash.texture = p.splashTextures[0] + # Position. + pos = gui_aux_cellScreenPosition(p.c, [0, 0]) + p.splash.left = pos[0] + p.splash.top = pos[1] +#} + +def desktop_createTitle(p): + p.title = arcade.Sprite() + p.titleSprites.append(p.title) + p.title.texture = p.titleTextures[0] + # Position. + pos = gui_aux_cellScreenPosition(p.c, p.c.titlePosition) + p.title.left = pos[0] + p.title.top = pos[1] + # Invisible by default. + p.title.visible = False +#} + +# Deselect mismatched tiles +# +# Conditions: +# 1. Time to deselect mismatched items +def desktop_deselectMismatchedTiles(p): + if ( + p.c.recentField == "deselectMismatchedTiles" + ): + for id in p.c.mismatchedItems: + p.deselectedTiles[id].visible = True + p.selectedTiles[id].visible = False + #} + #} +#} + +# Display description for the first selected tile +# +# Conditions: +# 1. tile has just been selected and it's the first one in pair +def desktop_displayDesc(p): + if ( + p.c.recentField == "selectedId" and + ( + len(p.c.selectedItems) == 0 or + len(p.c.selectedItems) == 2 + ) + ): + gid = p.c.playfieldItems[p.c.selectedId] + p.desc.texture = p.descTextures[gid] + p.desc.visible = True + #} +#} + +# Display ending splash screen +# +# Conditions: +# 1. Time to display victory screen +def desktop_displayEndingSplashScreen(p): + if ( + p.c.recentField == "displayEndingSplashScreen" + ): + p.splash.texture = p.splashTextures[1] + p.splash.visible = True + #} +#} + +# Hide deselected tile and show selected one +# +# Conditions: +# 1. tile has just been selected +def desktop_displaySelectedTile(p): + if ( + p.c.recentField == "selectedId" + ): + id = p.c.selectedId + p.deselectedTiles[id].visible = False + p.selectedTiles[id].visible = True + #} +#} + +# Display title for the first selected tile +# +# Conditions: +# 1. tile has just been selected and it's the first one in pair +def desktop_displayTitle(p): + if ( + p.c.recentField == "selectedId" and + ( + len(p.c.selectedItems) == 0 or + len(p.c.selectedItems) == 2 + ) + ): + gid = p.c.playfieldItems[p.c.selectedId] + p.title.texture = p.titleTextures[gid] + p.title.visible = True + #} +#} + +# Hide beginning splash screen +# +# Conditions: +# 1. Time to hide +def desktop_hideBeginningSplashScreen(p): + if ( + p.c.recentField == "hideBeginningSplashScreen" + ): + p.splash.visible = False + #} +#} + +# Hide description +# +# Conditions: +# 1. tiles has been mismatched or timed out to to hide after matching +def desktop_hideDesc(p): + if ( + p.c.recentField == "hideMatchingTiles" or + p.c.recentField == "mismatchedItems" + ): + p.desc.visible = False + #} +#} + +# Hide matching tiles +# +# Conditions: +# 1. Time to hide matching items +def desktop_hideMatchingTiles(p): + if ( + p.c.recentField == "hideMatchingTiles" + ): + for id in p.c.selectedItems: + p.deselectedTiles[id].visible = False + p.selectedTiles[id].visible = False + #} + #} +#} + +# Hide title +# +# Conditions: +# 1. tiles has been mismatched or timed out to to hide after matching +def desktop_hideTitle(p): + if ( + p.c.recentField == "hideMatchingTiles" or + p.c.recentField == "mismatchedItems" + ): + p.title.visible = False + #} +#} + +# Load description textures +def desktop_loadDescTextures(p): + texs = [] + for (id, td) in enumerate(p.c.descTextureDescriptions): + tex = arcade.load_texture( + td.fileName, + x = td.x, + y = td.y, + width = td.width, + height = td.height + ) + texs.append(tex) + #} + p.descTextures = texs +#} + +# Load beginning and ending textures +def desktop_loadSplashTextures(p): + texs = [] + for (id, td) in enumerate(p.c.splashTextureDescriptions): + tex = arcade.load_texture( + td.fileName, + x = td.x, + y = td.y, + width = td.width, + height = td.height + ) + texs.append(tex) + print(f"desktop_loadST: '{tex}'") + #} + p.splashTextures = texs +#} + +# Load tile textures +def desktop_loadTextures(p): + texs = [] + for (id, td) in enumerate(p.c.textureDescriptions): + tex = arcade.load_texture( + td.fileName, + x = td.x, + y = td.y, + width = td.width, + height = td.height + ) + texs.append(tex) + #} + p.textures = texs +#} + +# Load tiTLe textures +def desktop_loadTitleTextures(p): + texs = [] + for (id, td) in enumerate(p.c.titleTextureDescriptions): + tex = arcade.load_texture( + td.fileName, + x = td.x, + y = td.y, + width = td.width, + height = td.height + ) + texs.append(tex) + #} + p.titleTextures = texs +#} + +# Postpone deselection of mismatched tiles for better UX +# +# Conditions: +# 1. a pair of tiles has been mismatched +def desktop_scheduleDeselectionOfMismatchedTiles(p): + if ( + p.c.recentField == "mismatchedItems" + ): + p.sequentialTimer.schedule("deselectMismatchedTiles", True, p.c.deselectMismatchedTilesDelay) + #} +#} + +# Postpone display of the ending splash screen +# +# Conditions: +# 1. just finished removing all tiles +def desktop_scheduleDisplayOfEndingSplashScreen(p): + if ( + p.c.recentField == "victory" + ): + p.sequentialTimer.schedule("displayEndingSplashScreen", True, p.c.splashEndDelay) + #} +#} + +# Postpone hiding of matching tiles for better UX +# +# Conditions: +# 1. a pair of tiles has been matched +def desktop_scheduleHidingOfMatchingTiles(p): + if ( + p.c.recentField == "hiddenItems" + ): + p.sequentialTimer.schedule("hideMatchingTiles", True, p.c.hideMatchingTilesDelay) + #} +#} + +# Postpone hiding of the beginning splash screen +# +# Conditions: +# 1. just launched the game +def desktop_scheduleHidingOfBeginningSplashScreen(p): + if ( + p.c.recentField == "splashBeginTimeout" + ): + p.sequentialTimer.schedule("hideBeginningSplashScreen", True, p.c.splashBeginTimeout) + #} +#} + diff --git a/harm/desktop_Platform.py b/harm/desktop_Platform.py new file mode 100644 index 0000000..b796232 --- /dev/null +++ b/harm/desktop_Platform.py @@ -0,0 +1,22 @@ +import arcade + +class desktop_Platform: + def __init__(self): + self.c = None + self.ctrl = None + self.desc = None + self.descSprites = arcade.SpriteList() + self.descTextures = [] + self.deselectedSprites = arcade.SpriteList() + self.deselectedTiles = [] + self.mousePosition = [] + self.selectedTiles = [] + self.sequentialTimer = None + self.selectedSprites = arcade.SpriteList() + self.splash = None + self.splashSprites = arcade.SpriteList() + self.splashTextures = [] + self.textures = [] + self.title = None + self.titleSprites = arcade.SpriteList() + self.titleTextures = [] diff --git a/harm/desktop_SequentialTimer.py b/harm/desktop_SequentialTimer.py new file mode 100644 index 0000000..f4708b9 --- /dev/null +++ b/harm/desktop_SequentialTimer.py @@ -0,0 +1,35 @@ +import time + +class desktop_SequentialTimer(): + def __init__(self): + self.activeTimeout = None + self.callback = None + self.queue = [] + #} + + def update(self): + # Get current time in milliseconds. + now = time.time_ns() // 1000000 + + # Schedule an item. + if ( + self.activeTimeout == None and + len(self.queue) > 0 + ): + self.activeTimeout = now + self.queue[0][0] + elif ( + self.activeTimeout != None and + now >= self.activeTimeout + ): + # Report when the time is up. + key = self.queue[0][1] + value = self.queue[0][2] + self.queue.pop(0) + self.activeTimeout = None + self.callback(key, value) + #} + #} + + def schedule(self, key, value, timeout): + self.queue.append([timeout, key, value]) + #} diff --git a/harm/desktop_Window.py b/harm/desktop_Window.py new file mode 100644 index 0000000..8ca4165 --- /dev/null +++ b/harm/desktop_Window.py @@ -0,0 +1,32 @@ +import arcade +from desktop_aux import * + +class desktop_Window(arcade.Window): + def __init__(self, p): + super().__init__( + p.c.windowWidth, + p.c.windowHeight, + p.c.windowTitle, + ) + self.antialiasing = p.c.windowAntialiasing + self.background_color = arcade.color_from_hex_string(p.c.windowBackgroundColor) + self.p = p + + def on_draw(self): + arcade.start_render() + self.p.descSprites.draw() + self.p.deselectedSprites.draw() + self.p.selectedSprites.draw() + self.p.splashSprites.draw() + self.p.titleSprites.draw() + + def on_mouse_press(self, x, y, button, key_modifiers): + id = desktop_aux_tileIdAt(self.p, x, y) + if ( + id != None + ): + self.p.ctrl.set("selectedId", id) + + def on_update(self, delta): + self.p.deselectedSprites.update_animation() + self.p.sequentialTimer.update() diff --git a/harm/desktop_aux.py b/harm/desktop_aux.py new file mode 100644 index 0000000..de994b5 --- /dev/null +++ b/harm/desktop_aux.py @@ -0,0 +1,13 @@ +import arcade + +# Find a visible tile id at the specified location +def desktop_aux_tileIdAt(p, x, y): + sprites = arcade.get_sprites_at_point([x, y], p.deselectedSprites) + if ( + len(sprites) != 0 and + sprites[0].visible + ): + return sprites[0].guid + #} + return None +#} diff --git a/harm/gui.py b/harm/gui.py new file mode 100644 index 0000000..c5560b4 --- /dev/null +++ b/harm/gui.py @@ -0,0 +1,175 @@ +from gui_aux import * +from gui_TextureDescription import * +from llm import * +from memory_Context import * + +# Generate texture descriptions for harm description +# +# Conditions: +# 1.descImage, descImageCount, descImageHeight, or descImageWidth has just changed +@llm_by_value +def gui_generateDescTextureDescriptions( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "descImage" or + c.recentField == "descImageCount" or + c.recentField == "descImageHeight" or + c.recentField == "descImageWidth" + ): + tds: list[gui_TextureDescription] = [] + for id in range(0, c.descImageCount): + td = gui_createTextureDescription() + td.fileName = c.descImage + td.height = c.descImageHeight + td.width = c.descImageWidth + td.x = id * c.descImageWidth + td.y = 0 + tds.append(td) + #} + c.descTextureDescriptions = tds + c.recentField = "descTextureDescriptions" + return c + #} + + c.recentField = "none" + return c +#} + +# Generate texture descriptions for splash screens +# +# Conditions: +# 1.splashImage, splashImageCount, splashImageHeight, or splashImageWidth has just changed +@llm_by_value +def gui_generateSplashTextureDescriptions( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "splashImage" or + c.recentField == "splashImageCount" or + c.recentField == "splashImageHeight" or + c.recentField == "splashImageWidth" + ): + tds: list[gui_TextureDescription] = [] + for id in range(0, c.splashImageCount): + td = gui_createTextureDescription() + td.fileName = c.splashImage + td.height = c.splashImageHeight + td.width = c.splashImageWidth + td.x = id * c.splashImageWidth + td.y = 0 + tds.append(td) + #} + c.splashTextureDescriptions = tds + c.recentField = "splashTextureDescriptions" + return c + #} + + c.recentField = "none" + return c +#} + +# Generate texture descriptions for tiles +# +# Conditions: +# 1. tileImage or tileImageCount or tileImageHeight or tileImageWidth has just changed +@llm_by_value +def gui_generateTextureDescriptions( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "tileImage" or + c.recentField == "tileImageCount" or + c.recentField == "tileImageHeight" or + c.recentField == "tileImageWidth" + ): + tds: list[gui_TextureDescription] = [] + for id in range(0, c.tileImageCount): + td = gui_createTextureDescription() + td.fileName = c.tileImage + td.height = c.tileImageHeight + td.width = c.tileImageWidth + td.x = id * c.tileImageWidth + td.y = 0 + tds.append(td) + #} + c.textureDescriptions = tds + c.recentField = "textureDescriptions" + return c + #} + + c.recentField = "none" + return c +#} + +# Generate tile positions +# +# Conditions: +# 1. cellSize, playField, windowHeight, or windowWidth has changed and none of them is zero +@llm_by_value +def gui_generateTilePositions( + c: memory_Context +) -> memory_Context: + if ( + ( + c.recentField != "cellPositions" and + c.recentField != "cellSize" and + c.recentField != "playfieldSize" and + c.recentField != "windowHeight" and + c.recentField != "windowWidth" + ) or + ( + len(c.cellPositions) == 0 or + c.cellSize == 0 or + c.playfieldSize == 0 or + c.windowHeight == 0 or + c.windowWidth == 0 + ) + ): + c.recentField = "none" + return c + #} + + ps = [] + for i in range(0, len(c.cellPositions)): + p = gui_aux_cellScreenPosition(c, c.cellPositions[i]) + ps.append(p) + #} + + c.tilePositions = ps + c.recentField = "tilePositions" + return c +#} + +# Generate texture descriptions for tiTLes +# +# Conditions: +# 1. titleImage, titleImageCount, titleImageHeight, or titleImageWidth has just changed +@llm_by_value +def gui_generateTitleTextureDescriptions( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "titleImage" or + c.recentField == "titleImageCount" or + c.recentField == "titleImageHeight" or + c.recentField == "titleImageWidth" + ): + tds: list[gui_TextureDescription] = [] + for id in range(0, c.titleImageCount): + td = gui_createTextureDescription() + td.fileName = c.titleImage + td.height = c.titleImageHeight + td.width = c.titleImageWidth + td.x = id * c.titleImageWidth + td.y = 0 + tds.append(td) + #} + c.titleTextureDescriptions = tds + c.recentField = "titleTextureDescriptions" + return c + #} + + c.recentField = "none" + return c +#} diff --git a/harm/gui_TextureDescription.py b/harm/gui_TextureDescription.py new file mode 100644 index 0000000..ea15a43 --- /dev/null +++ b/harm/gui_TextureDescription.py @@ -0,0 +1,15 @@ +class gui_TextureDescription: + def __init__(self): + self.fileName = "" + self.height = 0 + self.width = 0 + self.x = 0 + self.y = 0 + def __repr__(self): + return self.__str__() + + def __str__(self): + return f"gui_TextureD(fileN/height/width/x/y: '{self.fileName}'/'{self.height}'/'{self.width}'/'{self.x}'/'{self.y}')" + +def gui_createTextureDescription(): + return gui_TextureDescription() diff --git a/harm/gui_aux.py b/harm/gui_aux.py new file mode 100644 index 0000000..970fbeb --- /dev/null +++ b/harm/gui_aux.py @@ -0,0 +1,78 @@ +from memory_Context import * + +# Generate grid positions in cell dimensions +# +# Conditions: +# 1. 2x2 grid +# 2. 4x4 grid +def gui_aux_cellGridPositions( + size: int +) -> [[int]]: + if ( + size == 2 + ): + return [ + [14, 7], + [19, 7], + [14, 13], + [19, 13], + ] + #} + + if ( + size == 4 + ): + return [ + [9, 1], + [14, 1], + [19, 1], + [24, 1], + + [9, 7], + [14, 7], + [19, 7], + [24, 7], + + [9, 13], + [14, 13], + [19, 13], + [24, 13], + + [9, 19], + [14, 19], + [19, 19], + [24, 19], + ] + #} +#} + +# Generate positions in cell dimensions for Harm game +def gui_aux_cellHarmPositions( +) -> [[int]]: + return [ + [4, 6], + [9, 6], + [14, 6], + [19, 6], + [24, 6], + [29, 6], + + [4, 12], + [9, 12], + [14, 12], + + [4, 18], + [9, 18], + [14, 18], + ] +#} + +# Convert cell position to screen position +def gui_aux_cellScreenPosition( + c: memory_Context, + pos: [int] +) -> [int]: + x = pos[0] * c.cellSize + y = c.windowHeight - pos[1] * c.cellSize + return [x, y] +#} diff --git a/harm/gui_aux_test.py b/harm/gui_aux_test.py new file mode 100644 index 0000000..393ed94 --- /dev/null +++ b/harm/gui_aux_test.py @@ -0,0 +1,15 @@ +from gui_aux import * + +def test_gui_aux_cellGridPositions( +) -> str: + items2x2 = gui_aux_cellGridPositions(2) + items4x4 = gui_aux_cellGridPositions(4) + + if ( + len(items2x2) == 4 and + len(items4x4) == 16 + ): + return "OK: gui_aux_cellGridPositions" + #} + return "ERR: gui_aux_cellGridPositions" +#} diff --git a/harm/gui_test.py b/harm/gui_test.py new file mode 100644 index 0000000..fcad14f --- /dev/null +++ b/harm/gui_test.py @@ -0,0 +1,49 @@ +from gui import * +from gui_TextureDescription import * +from memory_Context import * + +def test_gui_generateTextureDescriptions( +) -> str: + c = memory_createContext() + c.tileImage = "res/img.png" + c.tileImageCount = 3 + c.tileImageHeight = 35 + c.tileImageWidth = 20 + c.recentField = "tileImageWidth" + c = gui_generateTextureDescriptions(c) + if ( + c.recentField == "textureDescriptions" and + len(c.textureDescriptions) == 3 and + c.textureDescriptions[0].fileName == "res/img.png" and + c.textureDescriptions[0].height == 35 and + c.textureDescriptions[0].width == 20 and + c.textureDescriptions[0].x == 0 and + c.textureDescriptions[1].x == 20 and + c.textureDescriptions[2].x == 40 + ): + return "OK: gui_generateTextureDescriptions" + #} + return "ERR: gui_generateTextureDescriptions" +#} + +def test_gui_generateTilePositions( +) -> str: + c = memory_createContext() + + c.cellSize = 10 + c.playfieldSize = 2 + c.cellPositions = gui_aux_cellGridPositions(c.playfieldSize) + c.windowHeight = 200 + c.windowWidth = 200 + c.recentField = "windowWidth" + + c = gui_generateTilePositions(c) + if ( + c.recentField == "tilePositions" and + len(c.tilePositions) == 4 and + c.tilePositions[0][0] == 140 + ): + return "OK: gui_generateTilePositions" + #} + return "ERR: gui_generateTilePositions" +#} diff --git a/harm/llm.py b/harm/llm.py new file mode 100644 index 0000000..0ca860f --- /dev/null +++ b/harm/llm.py @@ -0,0 +1,22 @@ +import copy + +# Make deep copies of arguments to treat the arguments as structs. +# https://stackoverflow.com/a/15398021 +def llm_by_value(f): + def _f(*args, **kwargs): + argsCopy = copy.deepcopy(args) + kwargsCopy = copy.deepcopy(kwargs) + return f(*argsCopy, **kwargsCopy) + return _f + +# Tell if string is a digit +def llm_isDigit(s): + return s.isdigit() + +# Tell if string starts with certain prefix. +def llm_startsWith(s, prefix): + return s.startswith(prefix) + +# Convert string to integer +def llm_strToInt(s): + return int(s) diff --git a/harm/llm_test.py b/harm/llm_test.py new file mode 100644 index 0000000..17ee0ca --- /dev/null +++ b/harm/llm_test.py @@ -0,0 +1,31 @@ +from llm import * + +def llm_test_isDigit_digit( +) -> str: + if ( + llm_isDigit("123") + ): + return "OK: llm_isDigit_digit" + #} + return "ERR: llm_isDigit_digit" +#} + +def llm_test_isDigit_notDigit( +) -> str: + if ( + llm_isDigit("abc") + ): + return "ERR: llm_isDigit_notDigit" + #} + return "OK: llm_isDigit_notDigit" +#} + +def llm_test_strToInt( +) -> str: + if ( + llm_strToInt("123") == 123 + ): + return "OK: llm_strToInt" + #} + return "ERR: llm_strToInt" +#} diff --git a/harm/llm_test_Python.py b/harm/llm_test_Python.py new file mode 100644 index 0000000..8c0e39c --- /dev/null +++ b/harm/llm_test_Python.py @@ -0,0 +1,21 @@ +from llm import * +from memory_Context import * + +def llm_test_Python_copyByValue( +) -> str: + c = memory_createContext() + c.input = "abc" + + @llm_by_value + def alterValue(c): + c.input = "alteredValue" + + alterValue(c) + + if ( + c.input == "abc" + ): + return "OK: llm_Python_copyByValue" + #} + return "ERR: llm_Python_copyByValue" +#} diff --git a/harm/main-gui.py b/harm/main-gui.py new file mode 100644 index 0000000..a23a229 --- /dev/null +++ b/harm/main-gui.py @@ -0,0 +1,160 @@ +import arcade +from cli import * +from cli_test import * +from ctx import * +from ctx_test2 import * +from desktop import * +from desktop_Platform import * +from desktop_SequentialTimer import * +from desktop_Window import * +from gui import * +from gui_aux import * +from gui_aux_test import * +from gui_test import * +from llm_test import * +from llm_test_Python import * +from memory_test import * +import sys + +print(ctx_test_Controller_executeFunctions_registerFunction_set()) +print(ctx_test_Controller_processQueue()) +print(ctx_test_Controller_registerFieldCallback_match()) +print(ctx_test_Controller_registerFieldCallback_mismatch()) +print(ctx_test_memoryContext_field()) +print(ctx_test_memoryContext_setField()) + +print(llm_test_Python_copyByValue()) +print(llm_test_isDigit_digit()) +print(llm_test_isDigit_notDigit()) +print(llm_test_strToInt()) + +print(memory_test_detectMismatchedItems()) +print(memory_test_detectMismatchedItems_itemTwice()) +print(memory_test_detectVictory()) +print(memory_test_generateConstPlayfield()) +print(memory_test_hideMatchingItems()) +print(memory_test_selectItem_1x()) +print(memory_test_selectItem_2x()) +print(memory_test_selectItem_3x()) + +print(cli_test_exit_e()) +print(cli_test_exit_exit()) +print(cli_test_exit_victory()) +print(cli_test_exit_q()) +print(cli_test_exit_quit()) +print(cli_test_goOn()) +print(cli_test_greetUser()) +print(cli_test_showHelp_h()) +print(cli_test_showHelp_help()) +print(cli_test_selectItem()) +print(cli_test_promptSecondItemSelection()) +print(cli_test_reportMatchedItems()) +print(cli_test_reportMismatchedItems()) +print(cli_test_reportVictory()) + +print(test_gui_aux_cellGridPositions()) +print(test_gui_generateTextureDescriptions()) +print(test_gui_generateTilePositions()) + +ctrl = ctx_Controller(memory_createContext()) +ctrl.registerFunctions([ +# cli_exit, +# cli_goOn, +# cli_greetUser, +# cli_promptSecondItemSelection, +# cli_reportMatchedItems, +# cli_reportMismatchedItems, +# cli_reportVictory, +# cli_selectItem, +# cli_showHelp, + gui_generateDescTextureDescriptions, + gui_generateSplashTextureDescriptions, + gui_generateTextureDescriptions, + gui_generateTilePositions, + gui_generateTitleTextureDescriptions, + memory_detectMismatchedItems, + memory_detectVictory, + memory_generateConstPlayfield, + memory_hideMatchingItems, + memory_selectItem, +]) + +def printDbg(c): + print(f"Dbg key/value: '{c.recentField}'/'{c.field(c.recentField)}'") +ctrl.registerCallback(printDbg) +ctrl.registerFieldCallback("exit", lambda c: sys.exit(0)) + +p = desktop_Platform() +p.ctrl = ctrl +p.sequentialTimer = desktop_SequentialTimer() +p.sequentialTimer.callback = lambda key, value: ctrl.set(key, value) + +# Bind platform to context changes. +def process(c): + # Copy context to platform. + p.c = c + # Perform context dependent calls of desktop functions. + # Similar to context functions, but no platform is returned. + desktop_deselectMismatchedTiles(p) + desktop_displayDesc(p) + desktop_displayEndingSplashScreen(p) + desktop_displaySelectedTile(p) + desktop_displayTitle(p) + desktop_hideBeginningSplashScreen(p) + desktop_hideDesc(p) + desktop_hideMatchingTiles(p) + desktop_hideTitle(p) + desktop_scheduleDisplayOfEndingSplashScreen(p) + desktop_scheduleHidingOfBeginningSplashScreen(p) + desktop_scheduleHidingOfMatchingTiles(p) + desktop_scheduleDeselectionOfMismatchedTiles(p) +ctrl.registerCallback(process) + +ctrl.set("didLaunch", True) +ctrl.set("playfieldSize", 6) + +ctrl.set("cellPositions", gui_aux_cellHarmPositions()) +ctrl.set("cellSize", 25) +ctrl.set("deselectMismatchedTilesDelay", 500) +ctrl.set("descImage", "res/harm.png") +ctrl.set("descImageCount", 6) +ctrl.set("descImageHeight", 250) +ctrl.set("descImageWidth", 375) +ctrl.set("descPosition", [19, 12]) +ctrl.set("hideMatchingTilesDelay", 500) +ctrl.set("splashBeginTimeout", 2000) +ctrl.set("splashEndDelay", 100) +ctrl.set("splashImage", "res/splash.png") +ctrl.set("splashImageCount", 2) +ctrl.set("splashImageHeight", 600) +ctrl.set("splashImageWidth", 900) +ctrl.set("tileImage", "res/harmful-tiles.png") +ctrl.set("tileImageCount", 8) +ctrl.set("tileImageHeight", 100) +ctrl.set("tileImageWidth", 75) +ctrl.set("titleImage", "res/harmful-titles.png") +ctrl.set("titleImageCount", 6) +ctrl.set("titleImageHeight", 100) +ctrl.set("titleImageWidth", 700) +ctrl.set("titlePosition", [4, 1]) +ctrl.set("windowWidth", 900) +ctrl.set("windowHeight", 600) +ctrl.set("windowAntialiasing", False) +ctrl.set("windowBackgroundColor", "#ffffff") +ctrl.set("windowTitle", "Вредные продукты") + +desktop_loadTextures(p) +desktop_createDeselectedTiles(p) +desktop_createSelectedTiles(p) + +desktop_loadTitleTextures(p) +desktop_createTitle(p) + +desktop_loadDescTextures(p) +desktop_createDesc(p) + +desktop_loadSplashTextures(p) +desktop_createSplash(p) + +wnd = desktop_Window(p) +arcade.run() diff --git a/harm/main.py b/harm/main.py new file mode 100644 index 0000000..e3fdb56 --- /dev/null +++ b/harm/main.py @@ -0,0 +1,77 @@ +from cli import * +from cli_test import * +from ctx import * +from ctx_test2 import * +#from ctx_test import * +#from ctx_test_Python import * +from llm_test import * +from llm_test_Python import * +from memory_test import * +import sys + +print(ctx_test_Controller_executeFunctions_registerFunction_set()) +print(ctx_test_Controller_processQueue()) +print(ctx_test_Controller_registerFieldCallback_match()) +print(ctx_test_Controller_registerFieldCallback_mismatch()) +print(ctx_test_memoryContext_field()) +print(ctx_test_memoryContext_setField()) + +print(llm_test_Python_copyByValue()) +print(llm_test_isDigit_digit()) +print(llm_test_isDigit_notDigit()) +print(llm_test_strToInt()) + +print(memory_test_detectMismatchedItems()) +print(memory_test_detectMismatchedItems_itemTwice()) +print(memory_test_detectVictory()) +print(memory_test_generateConstPlayfield()) +print(memory_test_hideMatchingItems()) +print(memory_test_selectItem_1x()) +print(memory_test_selectItem_2x()) +print(memory_test_selectItem_3x()) + +print(cli_test_exit_e()) +print(cli_test_exit_exit()) +print(cli_test_exit_victory()) +print(cli_test_exit_q()) +print(cli_test_exit_quit()) +print(cli_test_goOn()) +print(cli_test_greetUser()) +print(cli_test_showHelp_h()) +print(cli_test_showHelp_help()) +print(cli_test_selectItem()) +print(cli_test_promptSecondItemSelection()) +print(cli_test_reportMatchedItems()) +print(cli_test_reportMismatchedItems()) +print(cli_test_reportVictory()) + +ctrl = ctx_Controller(memory_createContext()) +ctrl.registerFunctions([ + cli_exit, + cli_goOn, + cli_greetUser, + cli_promptSecondItemSelection, + cli_reportMatchedItems, + cli_reportMismatchedItems, + cli_reportVictory, + cli_selectItem, + cli_showHelp, + memory_detectMismatchedItems, + memory_detectVictory, + memory_generateConstPlayfield, + memory_hideMatchingItems, + memory_selectItem, +]) + +def printOutput(c): + if c.recentField.startswith("output"): + print(c.field(c.recentField)) +ctrl.registerCallback(printOutput) +ctrl.registerFieldCallback("exit", lambda c: sys.exit(0)) + +ctrl.set("didLaunch", True) +ctrl.set("playfieldSize", 2) + +for line in sys.stdin: + ln = line.rstrip() + ctrl.set("input", ln) diff --git a/harm/memory.py b/harm/memory.py new file mode 100644 index 0000000..e929d44 --- /dev/null +++ b/harm/memory.py @@ -0,0 +1,138 @@ +from memory_Context import * +from llm import * + +# Detect mismatched items +# +# Conditions: +# 0. Two items has just been selected +# 1. The same item has been selected twice +# 2. Selected items are of different groups +@llm_by_value +def memory_detectMismatchedItems( + c: memory_Context +) -> memory_Context: + if not ( + c.recentField == "selectedItems" and + len(c.selectedItems) == 2 + ): + c.recentField = "none" + return c + #} + if ( + c.selectedItems[0] == c.selectedItems[1] + ): + c.mismatchedItems.clear() + c.mismatchedItems.append(c.selectedItems[0]) + c.recentField = "mismatchedItems" + return c + #} + if ( + c.playfieldItems[c.selectedItems[0]] != c.playfieldItems[c.selectedItems[1]] + ): + c.mismatchedItems.clear() + c.mismatchedItems.append(c.selectedItems[0]) + c.mismatchedItems.append(c.selectedItems[1]) + c.recentField = "mismatchedItems" + return c + #} + c.recentField = "none" + return c +#} + +# Detect victory +# +# Conditions: +# 1. Matching items have just been hidden and all items are hidden now +@llm_by_value +def memory_detectVictory( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "hiddenItems" and + len(c.hiddenItems) == len(c.playfieldItems) + ): + c.victory = True + c.recentField = "victory" + return c + #} + c.recentField = "none" + return c +#} + +# Generate constant playfield +# +# Conditions: +# 1. Size has just been specified +# +# Both ids and group ids start with 0 +@llm_by_value +def memory_generateConstPlayfield( + c: memory_Context +) -> memory_Context: + if not ( + c.recentField == "playfieldSize" + ): + c.recentField = "none" + return c + #} + + idGroups: dict[int, int] = { } + id = 0 + for gid in range(0, c.playfieldSize): + idGroups[id] = gid + id += 1 + idGroups[id] = gid + id += 1 + #} + c.playfieldItems = idGroups + c.recentField = "playfieldItems" + return c +#} + +# Hide matching selected items +# +# Conditions: +# 1. Two items are selected and they are of the same group +@llm_by_value +def memory_hideMatchingItems( + c: memory_Context +) -> memory_Context: + if ( + c.recentField == "selectedItems" and + len(c.selectedItems) == 2 and + c.playfieldItems[c.selectedItems[0]] == c.playfieldItems[c.selectedItems[1]] + ): + c.hiddenItems.append(c.selectedItems[0]) + c.hiddenItems.append(c.selectedItems[1]) + c.recentField = "hiddenItems" + return c + #} + c.recentField = "none" + return c +#} + +# Select item +# +# Conditions: +# 1. Id has just been specified for selection +@llm_by_value +def memory_selectItem( + c: memory_Context +) -> memory_Context: + if not ( + c.recentField == "selectedId" + ): + c.recentField = "none" + return c + #} + + if ( + len(c.selectedItems) == 2 + ): + c.selectedItems.clear() + #} + c.selectedItems.append(c.selectedId) + c.recentField = "selectedItems" + return c +#} + diff --git a/harm/memory_Context.py b/harm/memory_Context.py new file mode 100644 index 0000000..ea646c4 --- /dev/null +++ b/harm/memory_Context.py @@ -0,0 +1,71 @@ +class memory_Context: + def __init__(self): + self.cellPositions = [] + self.cellSize = 0 + self.descImage = "" + self.descImageCount = 0 + self.descImageHeight = 0 + self.descImageWidth = 0 + self.descPosition = [] + self.descTextureDescriptions = [] + self.deselectMismatchedTiles = False + self.deselectMismatchedTilesDelay = 0 + self.didLaunch = False + self.displayEndingSplashScreen = False + self.exit = False + self.hiddenItems = [] + self.hideBeginningSplashScreen = False + self.hideMatchingTiles = False + self.hideMatchingTilesDelay = 0 + self.input = "" + self.mismatchedItems = [] + self.outputGoOn = "" + self.outputGreeting = "" + self.outputHelp = "" + self.outputMatchedItems = "" + self.outputMismatchedItems = "" + self.outputPromptSelection = "" + self.outputVictory = "" + self.playfieldItems = {} + self.playfieldSize = 0 + self.recentField = "none" + self.selectedId = -1 + self.selectedItems = [] + self.splashBeginTimeout = 0 + self.splashEndDelay = 0 + self.splashImage = "" + self.splashImageCount = 0 + self.splashImageHeight = 0 + self.splashImageWidth = 0 + self.textureDescriptions = [] + self.tileImage = "" + self.tileImageCount = 0 + self.tileImageHeight = 0 + self.tileImageWidth = 0 + self.tilePositions = [] + self.titleImage = "" + self.titleImageCount = 0 + self.titleImageHeight = 0 + self.titleImageWidth = 0 + self.titlePosition = [] + self.titleTextureDescriptions = [] + self.windowBackgroundColor = "#000000" + self.windowHeight = 0 + self.windowTitle = "" + self.windowWidth = 0 + self.victory = False + + def field(self, fieldName): + return getattr(self, fieldName) + + def setField(self, fieldName, value): + setattr(self, fieldName, value) + + def __repr__(self): + return self.__str__() + + def __str__(self): + return f"memory_Context(playfieldI/playfieldS/recentF/selectedId/selectedI: '{self.playfieldItems}'/'{self.playfieldSize}'/'{self.recentField}'/'{self.selectedId}'/'{self.selectedItems}')" + +def memory_createContext(): + return memory_Context() diff --git a/harm/memory_test.py b/harm/memory_test.py new file mode 100644 index 0000000..faa79ee --- /dev/null +++ b/harm/memory_test.py @@ -0,0 +1,231 @@ +from memory import * +from memory_Context import * + +def memory_test_detectMismatchedItems( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Select two items of different groups. + c.selectedId = 0 + c.recentField = "selectedId" + c = memory_selectItem(c) + c.selectedId = 2 + c.recentField = "selectedId" + c = memory_selectItem(c) + + # Detect mismatching. + c = memory_detectMismatchedItems(c) + + # See if the two selected items do not match. + if ( + c.recentField == "mismatchedItems" and + len(c.mismatchedItems) == 2 and + c.mismatchedItems[0] == 0 and + c.mismatchedItems[1] == 2 + ): + return "OK: memory_detectMismatchedItems" + #} + return "ERR: memory_detectMismatchedItems" +#} + +def memory_test_detectMismatchedItems_itemTwice( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Select the same item twice. + c.selectedId = 0 + c.recentField = "selectedId" + c = memory_selectItem(c) + c.selectedId = 0 + c.recentField = "selectedId" + c = memory_selectItem(c) + + # Detect mismatching. + c = memory_detectMismatchedItems(c) + + # See if the two selected items do not match. + if ( + c.recentField == "mismatchedItems" and + len(c.mismatchedItems) == 1 and + c.mismatchedItems[0] == 0 + ): + return "OK: memory_detectMismatchedItems_itemTwice" + #} + return "ERR: memory_detectMismatchedItems_itemTwice" +#} + +def memory_test_detectVictory( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Select the first two items of the same group. + c.selectedId = 0 + c.recentField = "selectedId" + c = memory_selectItem(c) + c.selectedId = 1 + c.recentField = "selectedId" + c = memory_selectItem(c) + + # Hide the first pair. + c = memory_hideMatchingItems(c) + + # Select the last two items of the same group. + c.selectedId = 2 + c.recentField = "selectedId" + c = memory_selectItem(c) + c.selectedId = 3 + c.recentField = "selectedId" + c = memory_selectItem(c) + + # Hide the second pair. + c = memory_hideMatchingItems(c) + + # Detect victory. + c = memory_detectVictory(c) + + # See if victory has been detected. + if ( + c.recentField == "victory" and + c.victory == True + ): + return "OK: memory_detectVictory" + #} + return "ERR: memory_detectVictory" +#} + +def memory_test_generateConstPlayfield( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + if ( + c.recentField == "playfieldItems" and + len(c.playfieldItems) == 4 and + c.playfieldItems[0] == 0 and + c.playfieldItems[1] == 0 and + c.playfieldItems[2] == 1 and + c.playfieldItems[3] == 1 + ): + return "OK: memory_generateConstPlayfield" + #} + return "ERR: memory_generateConstPlayfield" +#} + +def memory_test_hideMatchingItems( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Select two items of the same group. + c.selectedId = 0 + c.recentField = "selectedId" + c = memory_selectItem(c) + c.selectedId = 1 + c.recentField = "selectedId" + c = memory_selectItem(c) + + # Hide matching items. + c = memory_hideMatchingItems(c) + + # See if the two selected items match. + if ( + c.recentField == "hiddenItems" and + len(c.hiddenItems) == 2 and + c.hiddenItems[0] == 0 and + c.hiddenItems[1] == 1 + ): + return "OK: memory_hideMatchingItems" + #} + return "ERR: memory_hideMatchingItems" +#} + +def memory_test_selectItem_1x( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Select the first item. + c.selectedId = 0 + c.recentField = "selectedId" + c = memory_selectItem(c) + + # See if it's in selectedItems now. + if ( + c.recentField == "selectedItems" and + len(c.selectedItems) == 1 and + c.selectedItems[0] == 0 + ): + return "OK: memory_selectItem_1x" + #} + return "ERR: memory_selectItem_1x" +#} + +def memory_test_selectItem_2x( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Select the first two items. + c.selectedId = 0 + c.recentField = "selectedId" + c = memory_selectItem(c) + c.selectedId = 1 + c.recentField = "selectedId" + c = memory_selectItem(c) + + # See if both items are selected now. + if ( + c.recentField == "selectedItems" and + len(c.selectedItems) == 2 and + c.selectedItems[0] == 0 and + c.selectedItems[1] == 1 + ): + return "OK: memory_selectItem_2x" + #} + return "ERR: memory_selectItem_2x" +#} + +def memory_test_selectItem_3x( +) -> str: + c = memory_createContext() + c.playfieldSize = 2 + c.recentField = "playfieldSize" + c = memory_generateConstPlayfield(c) + + # Select three items. + c.selectedId = 0 + c.recentField = "selectedId" + c = memory_selectItem(c) + c.selectedId = 1 + c.recentField = "selectedId" + c = memory_selectItem(c) + c.selectedId = 2 + c.recentField = "selectedId" + c = memory_selectItem(c) + + # See if only one (last) item is selected now. + if ( + c.recentField == "selectedItems" and + len(c.selectedItems) == 1 and + c.selectedItems[0] == 2 + ): + return "OK: memory_selectItem_3x" + #} + return "ERR: memory_selectItem_3x" +#} diff --git a/harm/res/harm.png b/harm/res/harm.png new file mode 100644 index 0000000..8a09d25 Binary files /dev/null and b/harm/res/harm.png differ diff --git a/harm/res/harmful-tiles.png b/harm/res/harmful-tiles.png new file mode 100644 index 0000000..5e681ff Binary files /dev/null and b/harm/res/harmful-tiles.png differ diff --git a/harm/res/harmful-titles.png b/harm/res/harmful-titles.png new file mode 100644 index 0000000..add99cc Binary files /dev/null and b/harm/res/harmful-titles.png differ diff --git a/harm/res/splash.png b/harm/res/splash.png new file mode 100644 index 0000000..d7c5c8d Binary files /dev/null and b/harm/res/splash.png differ diff --git a/harm/run-gui-py b/harm/run-gui-py new file mode 100755 index 0000000..70e119b --- /dev/null +++ b/harm/run-gui-py @@ -0,0 +1,2 @@ +#!/bin/bash +/Users/mk/py3.12venv/bin/python3.12 main-gui.py