Dear Phillippe,
Thank you for your email, this was very helpful. I managed to get something working although it has been hacked together:
I make a subprocess call to the gnubg exe:
def call_gnubg(action: str = "session", match_ref: str = str(uuid4()), match_length: str = "1"):
"""
Calls the GNU Backgammon (gnubg) program from Python using the subprocess module.
Adjust the command and arguments according to your specific requirements.
"""
# Create a copy of the current environment variables
env = os.environ.copy()
# Set the custom environment variable
env["MATCH_REF"] = str(match_ref)
env["MATCH_LENGTH"] = str(match_length)
env["ACTION"] = str(action)
env["HOME"] = "/tmp" # required as gnubg needs to create .gnubg to run
# Command to run gnubg. Modify the command according to your use case.
# For example, you might want to run a specific command or script within gnubg.
command = ["/usr/games/gnubg", "-q", "-t", "-p", "libgnubg.py"]
# If you have specific arguments or need to interact with gnubg,
# add them to the command list. For example: ["gnubg", "--some-argument", "value"]
try:
# Run the command and wait for it to complete
result = subprocess.run(
command,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
env=env,
)
# Output the result (stdout)
print("GNU Backgammon output: %s", result.stdout)
# If needed, you can also handle errors using result.stderr
if result.stderr:
print("GNU Backgammon error output: %s", result.stderr)
except subprocess.CalledProcessError as e:
print("Error running GNU Backgammon: %s", e)
raise
except Exception as e:
print("An unexpected error occurred: %s", e)
raise
Which then calls a libgnubg from gnubg:
import base64
import gzip
import os
import json
import gnubg
def compress_data(data):
"""Compress data using gzip and encode it with base64."""
compressed_data = gzip.compress(data.encode("utf-8"))
return base64.b64encode(compressed_data).decode("utf-8")
def decompress_data(data):
"""Decode from base64 and decompress data using gzip."""
decoded_data = base64.b64decode(data)
decompressed_data = gzip.decompress(decoded_data)
return decompressed_data.decode("utf-8")
def read_game_content_from_file(file_path):
"""Read game content from a local file."""
with open(file_path, "r", encoding="utf-8") as file:
return file.read()
def delete_local_file(file_path):
"""Delete the specified file."""
try:
os.remove(file_path)
# print(f"File '{file_path}' was successfully deleted.")
except OSError as e:
print(f"Error: {e.strerror} - {e.filename}")
def write_dict_to_json_file(data_dict, match_ref):
"""
Writes the provided dictionary to a JSON file named after the match reference.
Parameters:
- data_dict (dict): The dictionary to be converted into a JSON string.
- match_ref (str): The reference name for the match, which is used as the filename.
The function writes the JSON representation of `data_dict` to a file named `{match_ref}.json`.
"""
# Define the filename based on match_ref
filename = f"/tmp/{match_ref}/{match_ref}.json"
# Write the dictionary to a JSON file
with open(filename, "w") as json_file:
json.dump(data_dict, json_file, indent=4)
def new_gnubg_match(match_length: str, match_ref: str) -> str:
"""
Creates a new GNU Backgammon match and saves it to a specified directory.
Parameters:
- match_length (int): The length of the backgammon match.
- match_ref (str): The reference name for the match, which is used to name the directory and match file.
Returns:
- None: The function prints the match details.
"""
# Create the directory for the match
match_dir = f"/tmp/{match_ref}"
os.makedirs(match_dir, exist_ok=True)
# Create a new match and save it
gnubg.command(f"new match {match_length}")
gnubg.command(f"save match {match_dir}/{match_ref}.sgf")
def new_gnubg_session(match_ref: str) -> str:
"""
Creates a new GNU Backgammon match and saves it to a specified directory.
Parameters:
- match_length (int): The length of the backgammon match.
- match_ref (str): The reference name for the match, which is used to name the directory and match file.
Returns:
- None: The function prints the match details.
"""
# Create the directory for the match
match_dir = f"/tmp/{match_ref}"
os.makedirs(match_dir, exist_ok=True)
# Create a new match and save it
gnubg.command(f"new session")
gnubg.command(f"save match {match_dir}/{match_ref}.sgf")
def load_match(match_ref: str):
gnubg.command(f"load match {match_ref}.sgf")
# print(dir(gnubg))
"""
data_dict = {
"sgf": compressed_data,
# board
# "board": gnubg.board(),
# calcgammonprice
# "gammonprice": gnubg.calcgammonprice(),
# cfevaluate
# "cfevaluate": gnubg.cfevaluate(),
# classifypos
# command
# cubeinfo
# dicerolls
# eq2mwc
# eq2mwc_stderr
# errorrating
# evalcontext
# evaluate
# findbestmove
# getevalhintfilter
# gnubgid
"gnubgid": gnubg.gnubgid(),
# hint
# luckrating
# match
"match": gnubg.match(),
# matchchecksum
"matchchecksum": gnubg.matchchecksum(),
# matchid
"matchid": gnubg.matchid(),
# met
# "met": gnubg.met(),
# movetupletostring
# mwc2eq
# "mwc2eq": gnubg.mwc2eq(),
# mwc2eq_stderr
# "mwc2eq_stderr": gnubg.mwc2eq_stderr(),
# navigate
# nextturn
# parsemove
# posinfo
# "posinfo": gnubg.posinfo(),
# positionbearoff
# positionfrombearoff
# positionfromid
# positionfromkey
# positionid
"positionid": gnubg.positionid(),
# positionkey
# rolloutcontext
# setevalhintfilter
# updateui
"""
if __name__ == "__main__":
match_ref = os.environ["MATCH_REF"]
match_length = os.environ["MATCH_LENGTH"]
action = "">
if action == "new_match":
new_gnubg_match(match_ref, match_length)
if action == "session":
new_gnubg_session(match_ref)
# Create game data for a log of the game
game_data = read_game_content_from_file(f"/tmp/{match_ref}/{match_ref}.sgf")
compressed_data = compress_data(game_data)
# Combine into dictionary
data_dict = {
"sgf": compressed_data,
"gnubgid": gnubg.gnubgid(),
"match": gnubg.match(),
"matchchecksum": gnubg.matchchecksum(),
"matchid": gnubg.matchid(),
"positionid": gnubg.positionid(),
}
# Write to file system
write_dict_to_json_file(data_dict, match_ref)
I have to write to the data to the file system, and use environment variables to pass in variables to the subprocess, but it seems to work.