Algorithms-Agents-and-Artif.../agents/red.py

103 lines
4.8 KiB
Python
Raw Normal View History

# Game Agents
import math
from random import Random
from typing import List, Tuple
from etc.messages import MESSAGE_UNDEFINED, RED_MESSAGES, Message, Opinion
from agents.green import GreenAgent
import etc.gamestate as gs
from agents.abstract_influencer_agent import AbstractInfluencerAgent
from agents.action_state import ActionState
from etc.util import RecursiveDict
from play_config import RED_AGENT_LUT_PATH
class RedAgent(AbstractInfluencerAgent):
# > A highly potent message may result in losing followers i.e., as compared
# > to the last round fewer green team members will be able to interact with
# > the red team agent
# Assume everyone is a follower at the start
red_followers: int # Number of green agents listening to red team
red_count: int # Number of green agents with Opinion.RED
rand: Random
def __init__(self, initial_followers: int, rand: Random) -> None:
super().__init__(RED_AGENT_LUT_PATH)
self.red_followers = initial_followers
self.red_count = 0
self.rand = rand
# > However, a potent message may decrease the uncertainity of opinion among
# > people who are already under the influence of the red team
@staticmethod
def red_action(person: GreenAgent, message: Message):
if person.following_red and person.polarity == Opinion.RED:
person.uncertainty -= message.potency
def influence(self, state: "gs.GameState", message: Message) -> None:
# No need to deepcopy() since each interaction only affects each person once.
self.last_message = message
for person in state.green_agents.nodes(data=False):
obj_person: GreenAgent = state.get_green_agent(person)
# > A highly potent message may result in losing followers i.e., as compared
# > to the last round fewer green team members will be able to interact
# > with the red team agent
if self.rand.random() < message.cost:
if obj_person.following_red:
obj_person.unfollow_red()
self.red_followers -= 1
# > However, a potent message may decrease the uncertainity of opinion among
# > people who are already under the influence of the red team
else:
RedAgent.red_action(obj_person, message)
# Random process
def dumb_influence(self, state: "gs.GameState") -> None:
self.influence(state, state.rand.choice(RED_MESSAGES))
def smart_influence(self, state: "gs.GameState") -> None:
red_opinion, blue_opinion = state.count_majority()
n_red_opinion_bin = ActionState.bin(red_opinion, ActionState.BINS, state.n_green_agents)
n_blue_opinion_bin = ActionState.bin(blue_opinion, ActionState.BINS, state.n_green_agents)
n_red_followers_bin = ActionState.bin(state.red_agent.red_followers, ActionState.BINS, state.n_green_agents)
blue_energy_bin = ActionState.bin(state.blue_agent.blue_energy, ActionState.BINS, state.blue_agent.initial_energy)
previous_rating: RecursiveDict[List[int, float]] = self.state_action_lut[n_red_opinion_bin][n_blue_opinion_bin][
n_red_followers_bin][blue_energy_bin]
n_samples = 1
largest_rating = -math.inf
largest_action = 0
for option in previous_rating:
option_rating_fractional = previous_rating[option]
rating = option_rating_fractional[1] / option_rating_fractional[0] # Average
if rating > largest_rating:
largest_rating = rating
largest_action = option
n_samples += option_rating_fractional[0]
message: Message
# Use 1/sqrt(x) - Central-limit theorem
if self.rand.uniform(0.0, 1.0) < 1 / math.sqrt(n_samples):
message = state.rand.choice(RED_MESSAGES)
else:
message = RED_MESSAGES[largest_action]
self.influence(state, message)
def choices() -> List[Tuple]:
return [(x) for x in RED_MESSAGES]
def update_short_term_mem(self, start_state: "gs.GameState", next_state: "gs.GameState") -> None:
self.short_term_mem.append(ActionState(self.last_message, start_state, next_state))
# ((Change in blue opinions) - (Change in red opinions) - (Change in Red followers) - (Change in blue energy)) * (Rounds to win)
# Red has the opposite heuristic
# There, learning. Are ya happy?????
def update_lut(self, terminal_state: "gs.GameState") -> None:
blue_winner = terminal_state.winner == gs.Winner.BLUE_WIN or terminal_state.winner == gs.Winner.RED_NO_FOLLOWERS
for action_state in self.short_term_mem:
# Since this is the red agent, assign negative to blue wins.
rating = -action_state.rate_state_action(terminal_state.iteration, blue_winner)
self.__update_lut_rating__(action_state, rating)