Skip to content

Commit

Permalink
Re-create #277 off of the main branch
Browse files Browse the repository at this point in the history
  • Loading branch information
stugots committed Nov 6, 2024
1 parent 98370ae commit c449a9e
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 65 deletions.
161 changes: 96 additions & 65 deletions dlgr/griduniverse/experiment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""The Griduniverse."""

import collections
import csv
import datetime
import itertools
import json
Expand Down Expand Up @@ -40,76 +40,77 @@
Bot = Bot

GU_PARAMS = {
"network": unicode,
"max_participants": int,
"bot_policy": unicode,
"num_rounds": int,
"time_per_round": float,
"instruct": bool,
"columns": int,
"rows": int,
"window_columns": int,
"window_rows": int,
"alternate_consumption_donation": bool,
"background_animation": bool,
"block_size": int,
"padding": int,
"bot_policy": unicode,
"build_walls": bool,
"chat_visibility_threshold": float,
"spatial_chat": bool,
"visibility": int,
"visibility_ramp_time": int,
"background_animation": bool,
"player_overlap": bool,
"leaderboard_group": bool,
"leaderboard_individual": bool,
"leaderboard_time": int,
"motion_speed_limit": float,
"motion_auto": bool,
"motion_cost": float,
"motion_tremble_rate": float,
"show_chatroom": bool,
"show_grid": bool,
"others_visible": bool,
"num_colors": int,
"mutable_colors": bool,
"costly_colors": bool,
"pseudonyms": bool,
"pseudonyms_locale": unicode,
"pseudonyms_gender": unicode,
"columns": int,
"contagion": int,
"contagion_hierarchy": bool,
"walls_density": float,
"walls_contiguity": float,
"walls_visible": bool,
"initial_score": int,
"costly_colors": bool,
"difi_group_image": unicode,
"difi_group_label": unicode,
"difi_question": bool,
"dollars_per_point": float,
"tax": float,
"relative_deprivation": float,
"frequency_dependence": float,
"frequency_dependent_payoff_rate": float,
"donation_amount": int,
"donation_individual": bool,
"donation_group": bool,
"donation_individual": bool,
"donation_ingroup": bool,
"donation_multiplier": float,
"donation_public": bool,
"difi_question": bool,
"difi_group_label": unicode,
"difi_group_image": unicode,
"frequency_dependence": float,
"frequency_dependent_payoff_rate": float,
"fun_survey": bool,
"pre_difi_question": bool,
"pre_difi_group_label": unicode,
"pre_difi_group_image": unicode,
"leach_survey": bool,
"intergroup_competition": float,
"intragroup_competition": float,
"identity_signaling": bool,
"identity_starts_visible": bool,
"initial_score": int,
"instruct": bool,
"intergroup_competition": float,
"intragroup_competition": float,
"leach_survey": bool,
"leaderboard_group": bool,
"leaderboard_individual": bool,
"leaderboard_time": int,
"map_csv": unicode,
"max_participants": int,
"motion_auto": bool,
"motion_cost": float,
"motion_speed_limit": float,
"motion_tremble_rate": float,
"mutable_colors": bool,
"network": unicode,
"num_colors": int,
"num_recruits": int,
"num_rounds": int,
"others_visible": bool,
"padding": int,
"player_overlap": bool,
"pre_difi_group_image": unicode,
"pre_difi_group_label": unicode,
"pre_difi_question": bool,
"pseudonyms": bool,
"pseudonyms_gender": unicode,
"pseudonyms_locale": unicode,
"relative_deprivation": float,
"rows": int,
"score_visible": bool,
"alternate_consumption_donation": bool,
"show_chatroom": bool,
"show_grid": bool,
"spatial_chat": bool,
"state_interval": float,
"tax": float,
"time_per_round": float,
"use_identicons": bool,
"build_walls": bool,
"visibility": int,
"visibility_ramp_time": int,
"wall_building_cost": int,
"donation_multiplier": float,
"num_recruits": int,
"state_interval": float,
"walls_contiguity": float,
"walls_density": float,
"walls_visible": bool,
"window_columns": int,
"window_rows": int,
}

DEFAULT_ITEM_CONFIG = {
Expand Down Expand Up @@ -201,6 +202,7 @@ def __init__(self, **kwargs):
self.visibility_ramp_time = kwargs.get("visibility_ramp_time", 4)
self.background_animation = kwargs.get("background_animation", True)
self.player_overlap = kwargs.get("player_overlap", False)
self.map_csv = kwargs.get("map_csv", None)

# Motion
self.motion_speed_limit = kwargs.get("motion_speed_limit", 8)
Expand Down Expand Up @@ -507,6 +509,23 @@ def compute_payoffs(self):
player.payoff *= inter_proportions[player.color_idx]
player.payoff *= self.dollars_per_point

def load_map(self):
with open(self.map_csv) as csv_file:
grid_state = self.csv_to_grid_state(csv_file)
self.deserialize(grid_state)

def csv_to_grid_state(self, csv_file):
grid_state = {}
reader = csv.reader(csv_file)

for i, row in enumerate(reader):
for j, col in enumerate(row):
# location = (i, j)
# Process each col value
continue

return grid_state

def build_labyrinth(self):
if self.walls_density and not self.wall_locations:
start = time.time()
Expand Down Expand Up @@ -1343,40 +1362,47 @@ def publish(self, msg):

def handle_connect(self, msg):
player_id = msg["player_id"]

if self.config.get("replay", False):
# Force all participants to be specatators
msg["player_id"] = "spectator"

if not self.grid.start_timestamp:
self.grid.start_timestamp = time.time()

if player_id == "spectator":
logger.info("A spectator has connected.")
return

logger.info("Client {} has connected.".format(player_id))
client_count = len(self.grid.players)
client_count = len(self.node_by_player_id)
logger.info("Grid num players: {}".format(self.grid.num_players))

if client_count < self.grid.num_players:
participant = self.session.query(dallinger.models.Participant).get(
player_id
)
network = self.get_network_for_participant(participant)

if network:
logger.info("Found an open network. Adding participant node...")
node = self.create_node(participant, network)
self.node_by_player_id[player_id] = node.id
self.session.add(node)
self.session.commit()
logger.info("Spawning player on the grid...")

# We use the current node id modulo the number of colours
# to pick the user's colour. This ensures that players are
# allocated to colours uniformly.
self.grid.spawn_player(
id=player_id,
color_name=self.grid.limited_player_color_names[
node.id % self.grid.num_colors
],
recruiter_id=participant.recruiter_id,
)
if player_id not in self.grid.players:
self.grid.spawn_player(
id=player_id,
color_name=self.grid.limited_player_color_names[
node.id % self.grid.num_colors
],
recruiter_id=participant.recruiter_id,
)
else:
logger.info("No free network found for player {}".format(player_id))

Expand Down Expand Up @@ -1721,9 +1747,14 @@ def send_state_thread(self):
def game_loop(self):
"""Update the world state."""
gevent.sleep(0.1)
if not self.config.get("replay", False):

if self.config.get("map_csv", None):
self.grid.load_map()

elif not self.config.get("replay", False):
self.grid.build_labyrinth()
logger.info("Spawning items")

for item_type in self.item_config.values():
for i in range(item_type["item_count"]):
if (i % 250) == 0:
Expand Down
10 changes: 10 additions & 0 deletions test/test_griduniverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@ def test_handle_connect_adds_player_to_grid(self, exp, a):
exp.handle_connect({"player_id": participant.id})
assert participant.id in exp.grid.players

def test_handle_connect_uses_existing_player_on_grid(self, exp, a):
participant = a.participant()
exp.grid.players[participant.id] = Player(
id=participant.id, color=[0.50, 0.86, 1.00], location=[10, 10]
)
exp.handle_connect({"player_id": participant.id})
assert participant.id in exp.node_by_player_id
assert len(exp.grid.players) == 1
assert len(exp.node_by_player_id) == 1

def test_handle_connect_is_noop_for_spectators(self, exp):
exp.handle_connect({"player_id": "spectator"})
assert exp.node_by_player_id == {}
Expand Down

0 comments on commit c449a9e

Please sign in to comment.