class Optimizer: def __init__(self): configs = load_configs("Configs.json") value_configs = load_configs("Configs_ValueNN.json") policy_configs = load_configs("Configs_PolicyNN.json") #logging.basicConfig(format='%(asctime)s %(message)s', filename=configs["log_name"], level=logging.WARNING, #datefmt="%Y-%m-%d %H:%M:%S") #logging.info("Starting Optimizer.") self.width = configs["width"] self.height = configs["height"] self.buffer_size = configs["buffer_size"] self.filename = configs["data_filename"] self.experiment_name = "Deviations/" + configs["val_filename"] self.buffer = Buffer(self.buffer_size) self.env = BlockRelocation(self.height, self.width) #self.model = ValueNetwork(configs=value_configs) #self.policy_network = PolicyNetwork(configs=policy_configs) #self.combined_model = CombinedModel(configs=configs) #self.value_wrapper = EstimatorWrapper(self.model) #self.policy_wrapper = EstimatorWrapper(self.policy_network) self.value_net = ValueNetworkKeras(value_configs) self.policy_net = PolicyNetworkKeras(policy_configs) self.tree_searcher = TreeSearch( self.value_net, BlockRelocation(self.height, self.width), self.policy_net) self.tree_searcher.std_vals = load_obj(self.experiment_name) self.baseline_params = { "search_depth": 5, "epsilon": 0.1, "threshold": 0.01, "drop_percent": 0.25, "factor": 0.01 } self.current_search_params = { "search_depth": 5, "epsilon": 0.1, "threshold": 0.05, "drop_percent": 0.3, "factor": 0.05 } self.dfs_params_hq = {"stop_param": 4, "k": 12} self.dfs_params_fast = {"stop_param": 1, "k": 12} #logging.info("Start up process complete.") def create_training_example(self, permutations=True, units=8): if units < self.height * self.width: matrix = self.env.create_instance_random(units) else: matrix = self.env.create_instance(self.height, self.width) if units < 6: path = self.tree_searcher.find_path_2(matrix.copy()) else: path = self.tree_searcher.find_path_dfs(matrix.copy()) # In case the solver can't solve it with the given depth, this function is called again if not path: return self.create_training_example(permutations=permutations, units=units) try: data = self.tree_searcher.move_along_path(matrix.copy(), path) except TypeError: return self.create_training_example(permutations=permutations, units=units) if permutations: data = self.create_permutations(data) else: data = self.prepare_data_for_model(data) return data, len(path) def train_on_new_instances(self, num=1, units=10, perm=False, train=False): data_list = [] step_list = [] for ii in range(num): data, steps = self.create_training_example(permutations=perm, units=units) data_list.append(data) step_list.append(steps) data = pd.concat(data_list, ignore_index=True, sort=False) self.calculate_deviations(data) #with open(self.filename, 'a') as f: # data.to_csv(f, header=False, index=False) if train: train_data = data.sample(int(data.shape[0] / 3)) self.value_net.train_df(train_data, epochs=2, validation=False) self.policy_net.train_df(train_data, epochs=2, validation=False) self.buffer.append(data) def calculate_deviations(self, data): data = data.copy() data["pred"] = list(self.value_net.predict_df(data)) data["pred"] = data["pred"].apply(lambda x: x[0]) data["Value"] = data["Value"].astype('int64') data["deviation"] = abs(data["pred"] - data["Value"]) deviations = data.groupby("Value")["deviation"].mean() new_vals = deviations.to_dict() old_vals = self.tree_searcher.std_vals for key in new_vals.keys(): if key not in old_vals: old_vals[key] = new_vals[key] else: old_vals[key] = (old_vals[key] + new_vals[key]) / 2 self.tree_searcher.std_vals = old_vals save_obj(old_vals, self.experiment_name) return old_vals def test_deviations(self): for i in range(20): data, steps = self.create_training_example(units=9) print(steps) print("DONE") print(self.calculate_deviations(data)) def prepare_data_for_model(self, data): # TODO DONT WANT TO CHANGE THE COLUMN NAME HERE data["StateRepresentation"] = data["StateRepresentation"].apply( lambda x: x.transpose().flatten()) data.columns = ["Moves", "StateRepresentation", "Value"] data["MovesEncoded"] = data["Moves"].copy() data["MovesEncoded"] = data["MovesEncoded"].apply( lambda x: self.tree_searcher.move_to_hot_one_encoding(x)) return data def create_permutations(self, df): df_list = [] for i, row in df.iterrows(): # creating representations rep = self.env.all_permutations_state(row.StateRepresentation) rep = list(rep) # creating value column val = [np.array(row.Value) for _ in range(len(rep))] # creating move and move_encoded columns moves = self.env.all_permutations_move(*row.Move) encoded = [ self.tree_searcher.move_to_hot_one_encoding(m) for m in moves ] # creating the DataFrame temp_df = pd.DataFrame({ "StateRepresentation": rep, "Value": val, "Moves": moves, "MovesEncoded": encoded }) # removing duplicates temp_df["hashable_state"] = temp_df.StateRepresentation.apply( lambda x: x.tostring()) temp_df = temp_df.drop_duplicates(subset="hashable_state") temp_df = temp_df.drop(columns="hashable_state") df_list.append(temp_df) final_df = pd.concat(df_list, ignore_index=True) return final_df def reinforce(self, iterations=20, units=12, instances=200, train=False): print("Starting reinforce with {} iterations and {} units.".format( iterations, units)) start = time.time() for x in range(iterations): print("Iteration " + str(x + 1)) self.train_on_new_instances(instances, units=units, train=train) end = time.time() print(end - start) with open("duration.txt", 'a+') as f: f.write( str(units) + " " + str(iterations * instances) + " " + str(end - start) + "\n") def train_and_update_models(self, epochs=20): data = self.buffer.get_sample(size=self.buffer.max_size) print("Training Policy Network ...") self.policy_net.train_df(data, epochs=epochs, validation=False) print("Training Value Network ...") self.value_net.train_df(data, epochs=epochs, validation=False) def train_on_csv(self, filename): data = pd.read_csv(filename) print(data.shape) data["StateRepresentation"] = data["StateRepresentation"].apply( lambda x: np.fromstring(x[1:-1], sep=" ")) data["MovesEncoded"] = data["MovesEncoded"].apply( lambda x: np.fromstring(x[1:-1], sep=" ")) data["hashed"] = data["StateRepresentation"].apply( lambda s: s.tostring()) data = data.drop_duplicates(subset="hashed") data = data.drop(columns=["hashed"]) data = data.reset_index(drop=True) print(data.shape) train_data, test_data = train_test_split(data, shuffle=True, test_size=0.1) for i in range(5): print("Currently on run {} of training.".format(i + 1)) self.policy_net.train_df(train_data) self.value_net.train_df(train_data) print("Policy Network Statistics:") print(self.value_net.eval(test_data)) print("Value Network Statistics:") print(self.policy_net.eval(test_data)) print("Training finished!") def full_experiment(self): total_container = self.width * self.height for ii in range(5): self.reinforce(iterations=5, units=5, instances=200, train=True) self.train_and_update_models() for ii in range(6, total_container - 5): print("Training: Currently training on {} units.".format(ii)) self.reinforce(iterations=20, units=ii, instances=2000) self.buffer.remove_duplicates() if ii % 3 == 0: data = self.buffer.get_sample(self.buffer.max_size, remove=True) self.policy_net.retrain_model(data) self.value_net.retrain_model(data) self.buffer.increase_max_size(0.1) del data else: self.train_and_update_models() for ii in range(total_container - 5, total_container + 1): print("Training: Currently training on {} units.".format(ii)) self.reinforce(iterations=20, units=ii, instances=2000) self.buffer.remove_duplicates() data = self.buffer.storage self.policy_net.retrain_model(data) self.value_net.retrain_model(data) self.buffer.increase_max_size(0.1) for ii in range(10): print("Training with all units. Currently on iteration ", str(ii + 1)) bm = Benchmark() bm.benchmark_caserta() self.reinforce(iterations=10, units=self.height * self.width, instances=2000) self.buffer.remove_duplicates() self.train_and_update_models() # find best parameters # run experiment on test instances def produce_testing_data(self, filename, examples=10000, perm=False): data_list = [] start = time.time() for e in range(examples): if e % 500 == 0 and e > 0: h = not os.path.isfile(filename) final_df = pd.concat(data_list) end = time.time() with open(filename, 'a+') as f: final_df.to_csv(f, header=h, index=False) print(end - start) start = time.time() data_list = [] data, length = self.create_training_example(permutations=perm, units=self.height * self.width) data_list.append(data) # in case number is not divisible by 500 final_df = pd.concat(data_list) with open(filename, 'a') as f: final_df.to_csv(f, header=False, index=False)
class MinerProtocol(Protocol): """ implementation of the Minecraft protocol """ def __init__(self, name): global debug self.log = debug self.bot = MinerBot(name, self.send) self.buffer = Buffer() self.world = World() self.packets = { # ID: (function, format 0x00: (self.onKeepAlive, Standard("Bi")), # Keep alive 0x01: (self.onLoginRequest, String("BiSqibbBB")), # Login request 0x02: (self.onHandshake, String("BS")), # Handshake 0x03: (self.onChat, String("BS")), # Chat message 0x04: (None, Standard("Bq")), # Time update 0x05: (None, Standard("Bihhh")), # Entity Equipment 0x06: (self.onSpawnPosition, Standard("Biii")), # Spawn position 0x07: (None, Standard("Bii?")), # Use entity (C --> S) 0x08: (self.onUpdateHealth, Standard("Bhhf")), # Update health 0x09: (None, Standard("Bbbbhl")), # Respawn (C --> S) 0x0A: (None, Standard("B?")), # Player (C --> S) 0x0B: (None, Standard("Bdddd?")), # Player position (C --> S) 0x0C: (None, Standard("Bff?")), # Player look (C --> S) 0x0D: (self.onPLayerPosition, Standard("Bddddff?")), # Player position & look 0x0E: (None, Standard("Bbibib")), # Player digging (C --> S) 0x0F: (None, BlockSlot("BibibW")), # Player block placement (C --> S) 0x10: (None, Standard("Bh")), # Holding change (C --> S) 0x11: (None, Standard("Bibibi")), # Use bed 0x12: (None, Standard("Bib")), # Animation 0x13: (None, Standard("Bib")), # Entity action (C --> S) 0x14: (self.onNamedEntitySpawn, String("BiSiiibbh")), # Named entity spawn 0x15: (None, Standard("Bihbhiiibbb")), # Pickup spawn 0x16: (None, Standard("Bii")), # Collect item 0x17: (self.onAddObject, Standard("Bibiiiihhh")), # Add object/vehicle 0x18: (None, MetaEntity("BibiiibbE")), # Mob spawn 0x19: (None, String("BiSiiii")), # Entity: painting 0x1A: (None, Standard("Biiiih")), # Experience Orb 0x1B: (None, Standard("Bffff??")), # Stance update 0x1C: (None, Standard("Bihhh")), # Entity velocity 0x1D: (None, Standard("Bi")), # Destroy entity 0x1E: (None, Standard("Bi")), # Entity 0x1F: (self.onEntityRelativeMove, Standard("Bibbb")), # Entity relative move 0x20: (None, Standard("Bibb")), # Entity look 0x21: (None, Standard("Bibbbbb")), # Entity look and relative move 0x22: (None, Standard("Biiiibb")), # Entity teleport 0x26: (None, Standard("Bib")), # Entity status 0x27: (None, Standard("Bii")), # Attach entity 0x28: (None, MetaEntity("BiE")), # Entity metadata 0x29: (None, Standard("Bibbh")), # Entity Effect 0x2A: (None, Standard("Bib")), # Remove Entity Effect 0x2B: (None, Standard("Bfhh")), # Experience 0x32: (self.onPreChunk, Standard("Bii?")), # Pre chunk 0x33: (self.onChunk, MetaChunk()), # Map chunk 0x34: (None, BlockArray()), # Multi-block change 0x35: (None, Standard("Bibibb")), # Block change 0x36: (None, Standard("Bihibb")), # Block action 0x3C: (None, BlockExplosion()), # Explosion 0x3D: (None, Standard("Biibii")), # Sound effect 0x46: (None, Standard("Bbb")), # New/invalid state 0x47: (None, Standard("Bi?iii")), # Thunderbolt 0x64: (None, String("BbbSb")), # Open window 0x65: (None, Standard("Bb")), # Close window 0x66: (None, BlockSlot("Bbhbh?W")), # Window click 0x67: (None, BlockSlot("BbhW")), # Set slot 0x68: (self.onWindowItems, Window()), # Window items 0x69: (None, Standard("Bbhh")), # Update window property 0x6A: (self.onTransaction, Standard("Bbh?")), # Transaction 0x6B: (None, BlockSlot("BhW")), # Creative inventory action 0x6C: (None, Standard("Bbb")), # Enchant Item 0x82: (None, String("BihiSSSS")), # Update sign 0x83: (None, ByteArray()), # item data 0xC8: (None, Standard("Bib")), # Increment statistic 0xC9: (self.onPlayerList, String("BS?h")), # Player List Item 0xFF: (self.onDisconnected, String("BS")), # Disconnect } def connectionMade(self): """ called when a connection has been established """ self.log.info("<%s> connected" % self.bot.getName()) # self.writer = PacketWriter(self.transport) self.log.debug("ATTEMPTING TO HANDSHAKE") self.sendHandshake(self.bot.getName()) def connectionLost(self, reason): self.log.error("connection lost %s" % reason) def dataReceived(self, data): """ new data are received """ self.buffer.append(data) while True: # get the packet Type try: data = self.buffer.peek()[0] packetType = ord(data) except IOError: # if empty buffer stop the loop break self.log.received(packetType) # get the packet information if packetType not in self.packets: self.log.error("Unknown packet 0x%02X" % packetType) self.transport.loseConnection() fct, fmt = self.packets[packetType] try: packet = fmt.unpack(self.buffer) except IOError: break # do action on packet received if fct != None: fct(self.bot, packet) def onDisconnected(self, bot, info): self.log.info("Disconnected by server. Reason=%s" % info[1]) def onKeepAlive(self, bot, info): self.send(0x00, 0) def onUpdateHealth(self, bot, info): bot.setHealth(health=info[1], food=info[2], foodSaturation=info[3]) def onLoginRequest(self, bot, info): bot.setUID(info[1]) #bot.setMaxSeed(info[3]) pass def onHandshake(self, bot, info): #bot.setConnectionHash(info[1]) self.sendLoginRequest(22, self.bot.getName()) self.bot.nameToUpper() def onSpawnPosition(self, bot, info): bot.setSpawnPosition(info) def onPLayerPosition(self, bot, info): cmd, x, stance, y, z, yaw, pitch, onGround = info bot.setPlayerPosition(x, y, z, stance, yaw, pitch, onGround) self.send(0x0D, x, y, stance, z, yaw, pitch, onGround) def onEntityRelativeMove(self, bot, info): bot.setEntityRelativeMove(info) def onWindowItems(self, bot, info): pass # if info[1] == 0: # # inventory window # bot.setInventory(info[2], info[3]) # else: # logging.error("Unknown window number=%d" % info[1]) # def onSetSlot(self, bot, info): # if info[1] == 0: # bot.setSlotItem(info[1], info[2], info[3], info[4], info[5]) # logging.debug("SetSlot %d, slot %d" %(info[1], info[2])) def onNamedEntitySpawn(self, bot, info): bot.setPlayerSpawn(info) def onAddObject(self): pass def onPreChunk(self, bot, info): pass def onChunk(self, bot, info): cmd, x, y, z, sx, sy, sz, compSize, chunkData = info # correct the real size sx += 1 sy += 1 sz += 1 self.log.chunk( "Chunk (x,y,z)=(%d, %d, %d) (sx,sy,sz)=(%d, %d, %d), len=%d" % (x, y, z, sx, sy, sz, len(chunkData))) self.world.addChunk(x, y, z, sx, sy, sz, chunkData) def onTransaction(self, bot, info): pass def send(self, cmd, *data): if cmd not in self.packets: self.log.error("Unknown packet to send 0x%02X" % cmd) self.transport.loseConnection() fct, pack = self.packets[cmd] res = pack.pack(cmd, *data) deb = "" for x in res: deb += "%02X " % ord(x) self.transport.write(res) self.log.send(cmd) def sendDisconnect(self, reason): self.send(0xFF, "Disconnection by client") self.transport.loseConnection() def sendHandshake(self, userName): self.send(0x02, userName) def sendLoginRequest(self, protocol_version, username): self.send(0x01, protocol_version, username, 0, 0, 0, 0, 0, 0) def onChat(self, bot, info): ''' Chat received. Analyse it to detect a message from the player ''' info = info[1].upper() if ord(info[0]) == 0xA7: #skip color code info = info[2:] # if the message is from a player it starts with <PlayerName> result = re.match("<(.*)>(.*)", info) if result == None: name = "" msg = info else: name = result.group(1) msg = result.group(2) result = msg.split() if BOT_NAME_NEEDED: # Command starts with the targeted Bot name if result[0] == bot.getName(): bot.parseCommand(name, result[1:]) else: if name <> bot.getName(): # do not compute self message bot.parseCommand(name, result) def onPlayerList(self, bot, info): bot.updatePlayerList(info[1], info[2])
class MinerProtocol(Protocol): """ implementation of the Minecraft protocol """ def __init__(self, name): global debug self.log = debug self.bot = MinerBot(name, self.send) self.buffer = Buffer() self.world = World() self.packets = { # ID: (function, format 0x00: (self.onKeepAlive, Standard("Bi")), # Keep alive 0x01: (self.onLoginRequest, String("BiSqibbBB")), # Login request 0x02: (self.onHandshake, String("BS")), # Handshake 0x03: (self.onChat, String("BS")), # Chat message 0x04: (None, Standard("Bq")), # Time update 0x05: (None, Standard("Bihhh")), # Entity Equipment 0x06: (self.onSpawnPosition, Standard("Biii")), # Spawn position 0x07: (None, Standard("Bii?")), # Use entity (C --> S) 0x08: (self.onUpdateHealth, Standard("Bhhf")), # Update health 0x09: (None, Standard("Bbbbhl")), # Respawn (C --> S) 0x0A: (None, Standard("B?")), # Player (C --> S) 0x0B: (None, Standard("Bdddd?")), # Player position (C --> S) 0x0C: (None, Standard("Bff?")), # Player look (C --> S) 0x0D: (self.onPLayerPosition, Standard("Bddddff?")), # Player position & look 0x0E: (None, Standard("Bbibib")), # Player digging (C --> S) 0x0F: (None, BlockSlot("BibibW")), # Player block placement (C --> S) 0x10: (None, Standard("Bh")), # Holding change (C --> S) 0x11: (None, Standard("Bibibi")), # Use bed 0x12: (None, Standard("Bib")), # Animation 0x13: (None, Standard("Bib")), # Entity action (C --> S) 0x14: (self.onNamedEntitySpawn, String("BiSiiibbh")), # Named entity spawn 0x15: (None, Standard("Bihbhiiibbb")), # Pickup spawn 0x16: (None, Standard("Bii")), # Collect item 0x17: (self.onAddObject, Standard("Bibiiiihhh")), # Add object/vehicle 0x18: (None, MetaEntity("BibiiibbE")), # Mob spawn 0x19: (None, String("BiSiiii")), # Entity: painting 0x1A: (None, Standard("Biiiih")), # Experience Orb 0x1B: (None, Standard("Bffff??")), # Stance update 0x1C: (None, Standard("Bihhh")), # Entity velocity 0x1D: (None, Standard("Bi")), # Destroy entity 0x1E: (None, Standard("Bi")), # Entity 0x1F: (self.onEntityRelativeMove, Standard("Bibbb")), # Entity relative move 0x20: (None, Standard("Bibb")), # Entity look 0x21: (None, Standard("Bibbbbb")), # Entity look and relative move 0x22: (None, Standard("Biiiibb")), # Entity teleport 0x26: (None, Standard("Bib")), # Entity status 0x27: (None, Standard("Bii")), # Attach entity 0x28: (None, MetaEntity("BiE")), # Entity metadata 0x29: (None, Standard("Bibbh")), # Entity Effect 0x2A: (None, Standard("Bib")), # Remove Entity Effect 0x2B: (None, Standard("Bfhh")), # Experience 0x32: (self.onPreChunk, Standard("Bii?")), # Pre chunk 0x33: (self.onChunk, MetaChunk()), # Map chunk 0x34: (None, BlockArray()), # Multi-block change 0x35: (None, Standard("Bibibb")), # Block change 0x36: (None, Standard("Bihibb")), # Block action 0x3C: (None, BlockExplosion()), # Explosion 0x3D: (None, Standard("Biibii")), # Sound effect 0x46: (None, Standard("Bbb")), # New/invalid state 0x47: (None, Standard("Bi?iii")), # Thunderbolt 0x64: (None, String("BbbSb")), # Open window 0x65: (None, Standard("Bb")), # Close window 0x66: (None, BlockSlot("Bbhbh?W")), # Window click 0x67: (None, BlockSlot("BbhW")), # Set slot 0x68: (self.onWindowItems, Window()), # Window items 0x69: (None, Standard("Bbhh")), # Update window property 0x6A: (self.onTransaction, Standard("Bbh?")), # Transaction 0x6B: (None, BlockSlot("BhW")), # Creative inventory action 0x6C: (None, Standard("Bbb")), # Enchant Item 0x82: (None, String("BihiSSSS")), # Update sign 0x83: (None, ByteArray()), # item data 0xC8: (None, Standard("Bib")), # Increment statistic 0xC9: (self.onPlayerList, String("BS?h")), # Player List Item 0xFF: (self.onDisconnected, String("BS")), # Disconnect } def connectionMade(self): """ called when a connection has been established """ self.log.info("<%s> connected" % self.bot.getName()) # self.writer = PacketWriter(self.transport) self.log.debug("ATTEMPTING TO HANDSHAKE") self.sendHandshake(self.bot.getName()) def connectionLost(self, reason): self.log.error("connection lost %s" % reason) def dataReceived(self, data): """ new data are received """ self.buffer.append(data) while True: # get the packet Type try: data = self.buffer.peek()[0] packetType = ord(data) except IOError: # if empty buffer stop the loop break self.log.received(packetType) # get the packet information if packetType not in self.packets: self.log.error("Unknown packet 0x%02X" % packetType) self.transport.loseConnection() fct, fmt = self.packets[packetType] try: packet = fmt.unpack(self.buffer) except IOError: break # do action on packet received if fct!= None: fct(self.bot, packet) def onDisconnected(self, bot, info): self.log.info("Disconnected by server. Reason=%s" % info[1]) def onKeepAlive(self, bot, info): self.send(0x00, 0) def onUpdateHealth(self, bot, info): bot.setHealth(health=info[1], food=info[2], foodSaturation=info[3]) def onLoginRequest(self, bot, info): bot.setUID(info[1]) #bot.setMaxSeed(info[3]) pass def onHandshake(self, bot, info): #bot.setConnectionHash(info[1]) self.sendLoginRequest(22, self.bot.getName()) self.bot.nameToUpper() def onSpawnPosition(self, bot, info): bot.setSpawnPosition(info) def onPLayerPosition(self, bot, info): cmd, x, stance, y, z, yaw, pitch, onGround = info bot.setPlayerPosition(x, y, z, stance, yaw, pitch, onGround) self.send(0x0D, x, y, stance, z, yaw, pitch, onGround) def onEntityRelativeMove(self, bot, info): bot.setEntityRelativeMove(info) def onWindowItems(self, bot, info): pass # if info[1] == 0: # # inventory window # bot.setInventory(info[2], info[3]) # else: # logging.error("Unknown window number=%d" % info[1]) # def onSetSlot(self, bot, info): # if info[1] == 0: # bot.setSlotItem(info[1], info[2], info[3], info[4], info[5]) # logging.debug("SetSlot %d, slot %d" %(info[1], info[2])) def onNamedEntitySpawn(self, bot, info): bot.setPlayerSpawn(info) def onAddObject(self): pass def onPreChunk(self, bot, info): pass def onChunk(self, bot, info): cmd, x, y, z, sx, sy, sz, compSize, chunkData = info # correct the real size sx += 1 sy += 1 sz += 1 self.log.chunk("Chunk (x,y,z)=(%d, %d, %d) (sx,sy,sz)=(%d, %d, %d), len=%d" % (x, y, z, sx, sy, sz, len(chunkData))) self.world.addChunk(x, y, z, sx, sy, sz, chunkData) def onTransaction(self, bot, info): pass def send(self, cmd, *data): if cmd not in self.packets: self.log.error("Unknown packet to send 0x%02X" % cmd) self.transport.loseConnection() fct, pack = self.packets[cmd] res = pack.pack(cmd, *data) deb = "" for x in res: deb += "%02X " % ord(x) self.transport.write(res) self.log.send(cmd) def sendDisconnect(self, reason): self.send(0xFF, "Disconnection by client") self.transport.loseConnection() def sendHandshake(self, userName): self.send(0x02, userName) def sendLoginRequest(self, protocol_version, username): self.send(0x01, protocol_version, username, 0, 0, 0, 0, 0, 0) def onChat(self, bot, info): ''' Chat received. Analyse it to detect a message from the player ''' info = info[1].upper() if ord(info[0]) == 0xA7: #skip color code info = info[2:] # if the message is from a player it starts with <PlayerName> result = re.match("<(.*)>(.*)", info) if result == None: name = "" msg = info else: name = result.group(1) msg = result.group(2) result = msg.split() if BOT_NAME_NEEDED: # Command starts with the targeted Bot name if result[0] == bot.getName(): bot.parseCommand(name, result[1:]) else: if name <> bot.getName(): # do not compute self message bot.parseCommand(name, result) def onPlayerList(self, bot, info): bot.updatePlayerList(info[1], info[2])
class Host(object): def __init__(self, number: int, gel: "Global Event List"): self.name = f"Host {number}" self.status = "idle" self.buffer = Buffer() self.channel = gel.channel self.GEL = gel self.arrivalRate = 0.8 # lambda self.senseTime = 0.01 # 0.01 ms self.DIFS = 0.1 # 0.10 ms self.SIFS = 0.05 # 0.05 ms self.notACKedDict = {} self.ackId = 0 def random_backoff(self, n: int)-> float: """ To generate random backoff counter value :param df: the dataframe which we can get the number of collision n from it :return: Random backoof counter value """ if n > 10: n = 10 wait_time = random.randint(0, 2 ** n - 1) return wait_time def addToBuffer(self, df: DataFrame): self.buffer.append(df) def processArrivalDataFrame(self, event_time: float, receiver: "Host", _type: str, df = None, origin = None): """ create one of the following arrival event and put it to the GEL event list internal DF: df created in this host success => schedule sense event to see if the channel is idle to process the df failure => put into the buffer external DF: df from external host to this host success => schedule receive event and then return an ACK latter failure => no failure ACK DF: ACK from external host success => take the next df from the buffer and process it failure => no failure """ sender = self success = None failure = None arrival = None if _type == "internal DF": """ Schedule next event To create a sense channel event, or put it into the buffer """ if self.GEL.packet_counter < self.GEL.TOTAL_PACKET: new_arrival_event = ScheduleDataFrameEvent(_type, event_time, sender, receiver, self.GEL, sender) self.GEL.addEvent(new_arrival_event) if self.status == "idle": sense_event_time = event_time + self.senseTime self.createSenseChannelEvent(sense_event_time, df, "df, stage 0", df.origin) else: self.buffer.insert_dataframe(df) elif _type == "external DF": """ create an ack packet, and then create a SenseChannel Event for this ack packet """ ack_time = event_time sense_event_time = event_time + self.senseTime # print("ack", ack_time, df.sender, df.receiver, df.id, df.origin) ack = DataFrame("ack", ack_time, df.sender, df.receiver, df.id, df.origin) ack.global_Id = df.global_Id ack.size = 64 self.createSenseChannelEvent(sense_event_time, ack, "ack, stage 0", df.origin) elif _type == "ack": success_time = event_time + 0 def success(): "to get the unacked event from the notAckedDict and then acknowledge the packet" unacked = self.notACKedDict[df.id] unacked.ACKed = True if len(self.buffer.array) != 0: next_df = self.origin.buffer.popleft() self.createSenseChannelEvent(event_time, next_df, "df, stage 0", df.origin) success_event = SuccessTransferEvent(success_time, df, success, failure, df.origin) self.GEL.addEvent(success_event) def createSenseChannelEvent(self, event_time: float, df: DataFrame, type: str, origin, counter: float = None): """ five types of sense event 1) df, stage 0 success => If the channel is idle, wait for 1 DIFS (0.1 ms) failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0. 2) df, stage 1 success => If the channel is idle again, transfer the dataframe to the channel immediately failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0. 3) ack, stage 0 success => If the channel is idle, wait for 1 SIFS (0.1 ms) failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0. 4) ack, stage 1 success => If the channel is idle again, transfer the ack to the channel immediately failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0. 5) Countdown (not finished) """ success = None failure = None sense_event_time = event_time + self.senseTime if type == "df, stage 0": """ 1) df, stage 0 success => If the channel is idle, wait for 1 DIFS (0.1 ms) failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0. """ def success(): self.createSenseChannelEvent(sense_event_time + self.DIFS, df, "df, stage 1", df.origin) def failure(): self.createSenseChannelEvent(sense_event_time, df, "RBA", df.origin) elif type == "df, stage 1": """ 2) df, stage 1 success => If the channel is idle again, transfer the dataframe to the channel immediately push_event_time_1 = event_time + transfer delay failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0. sense_event_time = event_time + self.senseTime """ def success(): push_event_time = sense_event_time self.createPushToChannelEvent(push_event_time, df) def failure(): _counter = self.random_backoff(df.number_of_collision) self.createSenseChannelEvent(sense_event_time, df, "Countdown", _counter) elif type == "ack, stage 0": """ 3) ack, stage 0 success => If the channel is idle, wait for 1 SIFS (0.1 ms) sense_time = event_time + 0.1 failure => If the channel is busy, do nothing """ def success(): self.createSenseChannelEvent(sense_event_time, df, "ack, stage 1", df.origin) def failure(): pass elif type == "ack, stage 1": """ 4) ack, stage 1 success => If the channel is idle again, transfer the ack to the channel immediately push_event_time = event_time + transfer delay failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0. """ def success(): push_event_time = sense_event_time self.createPushToChannelEvent(push_event_time, df, "ack") elif type == "Countdown": """ 5) Countdown (not finished) """ pass senseChannelEvent = SenseChannelEvent(sense_event_time, type, df, success, failure, df.origin ) self.GEL.addEvent(senseChannelEvent) def createPushToChannelEvent(self, event_time:float, df: DataFrame, type:str = "external DF"): """ After we detect the channel is idle again after DF stage 1 or ACK stage 1, we immediately push the packet into the channel pushEventTime = event_time + 0 There are two types of push event. One is external DF and the other is ack external DF: success => schedule a departure event and then expect an ACK packet return failure => no failure would occur ack: success => just schedule a departure event, no need to schedule an ACK expect event failure => no failure """ def success(): """ If success, expect an ACK packet return departure_event_time = event_time + transmission_time_df + transmission_time_ACK expected_ACK_time = event_time + (df size + ack size) / self.channel.rate + self.senseTime * 2 + 1e-9 1e-9 ensure that the expected_ACK_time must be behind the ACK received time :return: """ departure_event_time = event_time + df.size / self.channel.rate * 1000 self.channel.createDepartureEvent(departure_event_time, df, type) if type == "external DF": self.notACKedDict[df.id] = df self.createExpectAckEvent(event_time, df) elif type == "ack": """do not need to pass back ack if the packet is an ack""" pass def failure(): pass push_event_time = event_time + 0 pushEvent = PushToChannelEvent(push_event_time, f"push {df.type} to channel", df, success, failure, df.origin) self.GEL.addEvent(pushEvent) def createExpectAckEvent(self, event_time: float, df: DataFrame): """ If df is pushed to the channel, the sender expects an ACK packet will come back. This method is to create the AckExpecetedEvent and put it into the GEL. If the sender receives the ACK packet before timeout, then it does nothing; otherwise, it retransmit the packet success => do nothing failure => retransmit the packet (not finished) expected_event_time = event_time + total_transmission_time (2 df and 2 ACK) + SIFS + 2 * senseTime """ expected_event_time = event_time + (df.size + 64) / self.channel.rate * 1000 + self.SIFS + 0.0001 self.notACKedDict[df.id] = df _ackExpectEvent = AckExpectedEvent(event_time, expected_event_time, df, origin = df.origin) def success(): """to remove the object from the unACK Array""" result = AckResultEvent(expected_event_time, df, df.origin, "success") self.GEL.addEvent(result) # del self.notACKedDict[df.id] # del _ackExpectEvent def failure(): """ to remove the object from the unACK Array Then, use the RBA to create a counter and retransmit the object """ result = AckResultEvent(expected_event_time, df, df.origin, "failure") self.GEL.addEvent(result) df.number_of_collision += 1 counter = self.random_backoff(df.number_of_collision) # print(df.number_of_collision, counter) self.createSenseChannelEvent(event_time, df, "Countdown", counter) # del self.notACKedDict[df.id] _ackExpectEvent.success = success _ackExpectEvent.failure = failure _ackExpectEvent._id = df.id _ackExpectEvent.ACKed = False self.notACKedDict[df.id] = _ackExpectEvent self.GEL.addEvent(_ackExpectEvent) def __str__(self): return self.name def __repr__(self): return str(self.name)