Action Management ~ End Conditions Design - uchicago-cs/chiventure GitHub Wiki

Team Action Management - End Conditions Design (Eric Chang)


Introduction

As of writing this, the only way to end a game is if a player moves into a "final room." This document describes the design of an additional game-ending mechanism: the game ends if specific items have been interacted with such that their attributes have been changed to an "end state." After the implementation of this feature, a game can end in three different ways:

  1. A player moves into a "final room"
  2. Specific items have been interacted with appropriately
  3. A combination of the above two methods

Mechanism Overview

How will this new mechanism be implemented?

  • The file include/game-state/item.h includes definitions for a game_action_condition_t struct, which would appropriately store an end condition. This struct contains:
    • a pointer to an item
    • a pointer to an attribute to check
    • the expected value of the attribute
  • include/game-state/item.h also contains a definition for an action_condition_list_t, which would appropriately store a list of end conditions.
  • This list will be stored in the game_t struct defined in include/game-state/game.h as the field action_condition_list_t *end_conditions.

How can end conditions be added to a game?

The following function (which would add an end condition to a game's list of end conditions) would be added to game state's game module:

int add_end_condition_to_game(game_t *game, game_action_condition_t *end_condition)

Parameters:

  • game - the game to which end_condition should be added to
  • end_condition - the new end condition for this game

Returns:

  • FAILURE if the end condition cannot be added to the game. This can occur if:
    • the item in end_condition is not already part of the game
    • the attribute to check does not exist or is not an attribute of the item
  • SUCCESS if the end_condition is successfully added to the game.

A note to WDL: since add_end_condition_to_game requires the item referenced in end_condition to already be a part of a game, end conditions should not be added to a game until after items have been added.

How will end conditions be checked during a game?

The following function (which would check if all end conditions have been met) would be added to game state's game module:

bool end_conditions_met(game_t *game)

Parameters:

  • game - the game to check the end conditions of

Returns:

  • true if:
    • all end conditions have attributes with their expected values
    • no end conditions exist (end_conditions is NULL)
  • false if the attribute of at least one end condition does not match with its expected value

How will end conditions be removed from a game?

The following function (which would delete and free all game_action_condition_ts from an action_condition_list_t) would be added to game state's item module:

int delete_action_condition_llist(action_condition_list_t *head)

Parameters:

  • head - pointer to the first element in the list

Returns:

  • SUCCESS always

Utilizing this Mechanism

  • add_end_condition_to_game
    • Used by WDL when loading games into chiventure
    • Note that this function must be used AFTER loading in items into a game
    • While I go into detail in how the following functions are used, I am not doing that with this function. Determining how exactly add_end_condition_to_game will be utilized is a problem for WDL to work out. Since I am on the action management team and I will already be changing the code of game state to implement the functions, I or my team will likely be doing the work of integrating the other two functions into the code, so I went ahead and thought about how this can be done.
  • end_conditions_met
    • Used by action management in the actionmanagement module after an action is successfully completed to determine if the game is over
    • In do_item_action and do_item_item_action, in the else block that executes if the action is successfully carried out:
      • Use sprintf to add game_act->success_str to string (note that this code is already implemented)
      • Check if the final room exists AND the current room is the final room AND end_conditions_met returns true
        • If this test evaluates to true, append a congratulatory message (similar to that on line 168) to string
      • Set the value of ret_string to that of string (note that this code is already implemented)
      • Return SUCCESS (note that this code is already implemented)
    • In do_path_action:
      • Switch the placements of the if (move == SUCCESS) and else if (move == FINAL_ROOM) so that the check for the final room comes first
      • Change the conditional statement move == FINAL_ROOM to move == FINAL_ROOM && end_conditions_met(g)
      • Change the conditional statement move == SUCCESS to move == SUCCESS || move == FINAL_ROOM
  • delete_action_condition_llist
    • Used by game state in the game_free function found in the game module to free the end_conditions field of a game.
    • I don't know if it matters whether this function is called before delete_all_rooms or after since both might be trying to free the same items - keep this in mind as a likely culprit if there's some nasty looking error from this area of the code

Implementation Details

  • int add_end_condition_to_game(game_t *game, game_action_condition_t *end_condition):
    • Check that end_condition->item exists using HASH_FIND
      • If item does not exist, return FAILURE
    • Check that the end_condition->item contains end_condition->attribute_to_check
      • If this check fails, return FAILURE
      • Get attribute key, use get_attribute, see if return value and original attribute point to same value/place in memory
    • Set end_condition->next to game->end_conditions
    • Set game->end_conditions to end_condition
  • bool end_conditions_met(game_t *game)
    • If game->end_conditions is NULL return true
    • Create new pointer iterator to game->end_conditions
    • Loop while iterator->next is not NULL:
      • if not check_condition(iterator) return false
    • At this point, all conditions have been checked and confirmed to be their expected values - return true
  • int delete_action_condition_llist(action_condition_list_t *head):
    • For each node, free node
    • I don't think freeing item or attribute_to_check is necessary since delete_all_rooms should free them (again though if there's an error I suspect this would be a cause)

Final Notes

  • To use end_conditions_met, the headers of do_item_action and do_item_item_action would need to be changed so that it is passed an additional parameter: a context struct chiventure_ctx_t *c like that found as the first parameter of do_path_action
    • A search via get grep -n do_item_action reveals that the only places in code that use this function are in:
      1. src/cli/src/operations.c:161 - the function that contains this line of code is itself passed a context struct, so updating the call would be simple
      2. tests/action_management/test_item_actions.c - this would require creating a dummy context struct to pass along
    • A search via get grep -n do_item_item_action reveals that the only places in code that use this function are in:
      1. src/cli/src/operations.c:231 - the function that contains this line of code is itself passed a context struct, so updating the call would be simple
      2. tests/action_management/test_item_item_actions.c - again, this would require creating a dummy context struct to pass along
  • I don't have any other actual notes. This design is probably way more detailed than is actually necessary, but I wanted to get as many details ironed out as possible dangit.