Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion textworld/envs/glulx/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

115 changes: 56 additions & 59 deletions textworld/envs/wrappers/glulx_logger.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.

import json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a blank space after the license.


from typing import Tuple, List, Optional, Iterable, Union, Sized, Any, Mapping
Expand All @@ -9,20 +10,21 @@


class GameLog:
"""
GameLog object. Allows you to load and save previous game logs.
"""
def __init__(self):
"""
GameLog object. Allows your to load and save previous game logs.
"""
self._logs = [[]]
self._current_game = self._logs[-1]
self._filename = ''

def __getitem__(self, idx: int) -> list:
"""
"""A
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Gets a particular game log at index idx.

Args:
idx: index to retrieve
Returns:

Returns: list of logs at idx

"""
assert idx <= len(self._logs)
Expand All @@ -35,63 +37,69 @@ def __len__(self) -> int:
def current_game(self) -> list:
"""
Gets current game we're logging.

Returns: list of logs from current game.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a newline after Returns:, then the next line is indented (ref).

"""
return self._current_game
return self._logs[-1]

@property
def logs(self) -> list:
"""
Get all logs from all games.

Returns: All logs from all games.
"""
return self._logs

def new_game(self):
def new_game(self) -> list:
"""
Start logs for a new game.

Returns: log object for current game.
"""
if len(self._current_game) > 0:
if len(self.current_game) > 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you need this check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need this check to see if the current game is already new. We initialize our logs with a game inside, so there would be a redundancy if we didn't do this check.

self._logs.append([])
self._current_game = self._logs[-1]
return self._current_game
return self.current_game

def set(self, key: Any, value: Any) -> None:
"""
Sets value for latest game

Args:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a blank line between the description and the Args:

key: Key to set
value: Value to set

"""
current = self._current_game[-1]
current = self.current_game[-1]
current[key] = value

def append_optional(self, value: Any) -> None:
"""
Appends optional information to current game

Args:
value: Value to append
value: Value to append. Must be JSON serializable.

"""
current = self._current_game[-1]
current = self.current_game[-1]
if 'optional' not in current:
current['optional'] = []
current['optional'].append(value)

def add_log(self, log: Mapping):
"""
Adds a new log to our logs

Args:
log: Mapping of a log

"""
self._current_game.append(log)
self.current_game.append(log)

def save(self, filename):
def save(self, filename: str) -> None:
"""
Save current logs to specified file name

Args:
filename: File path to save to (should have JSON extension)

Expand All @@ -103,39 +111,42 @@ def save(self, filename):
except TypeError as e:
raise TypeError('Log not serializable')

def load(self, filename):
def load(self, filename: str) -> None:
"""
Loads a JSON object as logs

Args:
filename: file path to load.

"""
self._filename = filename
with open(filename) as f:
self._logs= json.load(f)
self._logs = json.load(f)


class GlulxLogger(Wrapper):
def __init__(self, env: GitGlulxMLEnvironment) -> None:
"""
Wrap around a TextWorld GitGlulxML environment to provide logging capabilities.
"""
Wrapper around a TextWorld GitGlulxML environment to provide logging capabilities.

Args:
env: The GitGlulxML environment to wrap.
"""
Args:
env: The GitGlulxML environment to wrap.
"""
def __init__(self, env: GitGlulxMLEnvironment) -> None:
super().__init__(env)
self.activate_state_tracking()

self.serialized_game = env.game.serialize()
self._gamefile = env.gamefile

self._logs = GameLog()
self._game_log = GameLog()

def step(self, command: str) -> Tuple[GlulxGameState, float, bool]:
"""
Take a step in the environment.

Args:
command: input string for taking an action

Returns:
GlulxGameState, score and done.
"""
Expand All @@ -150,72 +161,74 @@ def step(self, command: str) -> Tuple[GlulxGameState, float, bool]:
new_log['description'] = game_state.description
new_log['inventory'] = game_state.inventory
new_log['state'] = game_state.state.serialize()
self._logs.add_log(new_log)
self._game_log.add_log(new_log)

return game_state, score, done

def reset(self) -> GameState:
"""
Reset the environment.
Adds a new game into the logs.

Returns:
GameState
"""
new_log = {}
self._logs.new_game()
self._game_log.new_game()

game_state = super().reset()
new_log['optional'] = []
new_log['done'] = False
new_log['description'] = game_state.description
new_log['inventory'] = game_state.inventory
new_log['state'] = game_state.state.serialize()
self._logs.add_log(new_log)
self._game_log.add_log(new_log)

return game_state

def add_commands(self, commands: List[str], scores: Optional[Union[Iterable[float], Sized]]=None) -> None:
def add_commands(self, commands: List[str], scores: Optional[Iterable[float]]=None) -> None:
"""
Add custom commands to the logger. Optionally add scores for each command.

Args:
commands: A list of commands.
scores: scores for each command. Must be same size as commands if provided.

"""
if scores is not None:
self._logs.set('command_scores', scores)
self._game_log.set('command_scores', scores)

self._logs.set('commands', commands)
self._game_log.set('commands', commands)

def add(self, info: Any) -> None:
"""
Add any additional information you want to log.

Args:
info: Additional information to log for the current game state.
"""
self._logs.append_optional(info)
self._game_log.append_optional(info)

@property
def current(self) -> Mapping:
"""
Returns:
Current game state logs.
"""
return self._logs.current_game[-1]
return self._game_log.current_game[-1]

@property
def logs(self) -> List[Mapping]:
"""
Returns: List of all logs from this game.
"""
return self._logs.current_game
return self._game_log.current_game

@property
def all_logs(self) -> GameLog:
"""
Returns: GameLog object containing all logs.
"""
return self._logs
return self._game_log

@property
def gamefile(self) -> str:
Expand All @@ -228,41 +241,25 @@ def gamefile(self) -> str:
def __getitem__(self, index: int) -> Mapping:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we need this anymore.

"""
Get a certain log at a given index.

Args:
index: index of log to get.

Returns:
log at index
"""
assert index <= len(self._logs)
assert index <= len(self._game_log)

return self._logs.current_game[index]
return self._game_log.current_game[index]

def __str__(self) -> str:
return str(self._logs.current_game)
return str(self._game_log.current_game)

def serialize(self) -> List[Mapping]:
"""
Get serialized mappings of logs.

Returns:
List of serialized mappings.
"""
return self._logs.logs

def save(self, filename) -> None:
"""
Saves all logs given a filename
Returns: None
"""
self._logs.save(filename)

def load(self, filename) -> None:
"""
Loads logs from a file
Args:
filename:
string representing file location
Returns: None
"""
self._logs.load(filename)


return self._game_log.logs