Beispiel #1
0
def founding_order(the_line, groups, debug=False):
	results = order_block.default_line_results(the_line)
	
	all_cities				= the_line.the_world.cities()
	city_dict				= the_line.the_world.cities_from_team(the_line.block.team)
	cities_lookup			= the_line.the_world.cities_lookup(lower=True)
	
	the_team				= the_line.the_world.teams()[the_line.block.team]
	team_dict				= the_line.the_world.teams()
	
	dead_city = -1
	
	# Handles
	new_city_name = groups['city_name']
	supply_list_s = groups['city_list']
	size = int(groups['size'].replace(',', ''))
	if groups['city_type'] != None:
		city_type = groups['city_type'].lower().strip()
	else:
		city_type = None
	
	is_port, is_nomadic = False, False
	
	if city_type == "port":
		is_port = True
	elif city_type == "nomadic":
		is_nomadic = True
	
	# Get the location from the string
	try:
		x, y = groups['location'].split(',')
		x = int(x.strip())
		y = int(y.strip())
		terrain = mapper_q.get_terrain(the_line.the_world.cursor, x, y)
		
	except Exception as e:
		return order_block.fail(results, "%s was not founded because trying to read the location produced an error: %s" % (groups['city_name'], e.args[0]))
	
	# Rule checks
	#------------------------
	results = order_block.default_line_results(the_line, "%s could not be founded at %s because" % (new_city_name, str((x, y))))
	
	# You can't build on water!
	if map_data.terrain[terrain] in ('water', 'lake', 'XYZ_1', 'XYZ_2', 'XYZ_3'):
		return order_block.fail(results, "you cannot build on that terrain (%s)" % map_data.terrain[terrain])
	
	# Does it already exist?
	if new_city_name.lower() in cities_lookup:
		existing_city = all_cities[cities_lookup[new_city_name.lower()]]
		
		try:
			if not existing_city.dead:
				return order_block.fail(results, "%s already exists (controlled by %s)" % (new_city_name, team_dict[existing_city.team].name))
			else:
				# Currently all dead cities need a GM to act
				return order_block.fail(results, "%s already exists as a dead city, contact Teifion to to fix it (ID:%d)" % (new_city_name, cities_lookup[new_city_name.lower()]))
				
				
				if all_cities[cities_lookup[new_city_name.lower()]].team == the_team.id:
					dead_city = cities_lookup[new_city_name.lower()]
				else:
					return order_block.fail(results, "%s already exists as a dead city, contact Teifion to to fix it (ID:%d)" % (new_city_name, cities_lookup[new_city_name.lower()]))
		except Exception as e:
			print(new_city_name.lower())
			raise
	
	# Is it allowed to be a port?
	if is_port:
		if map_data.terrain[terrain] != "shore":
			is_port = False
			if mapper_q.get_terrain(the_line.the_world.cursor, x-10, y-10) == 0: is_port = True
			if mapper_q.get_terrain(the_line.the_world.cursor, x-10, y) == 0: is_port = True
			if mapper_q.get_terrain(the_line.the_world.cursor, x-10, y+10) == 0: is_port = True
			
			if mapper_q.get_terrain(the_line.the_world.cursor, x, y-10) == 0: is_port = True
			if mapper_q.get_terrain(the_line.the_world.cursor, x, y+10) == 0: is_port = True
			
			if mapper_q.get_terrain(the_line.the_world.cursor, x+10, y-10) == 0: is_port = True
			if mapper_q.get_terrain(the_line.the_world.cursor, x+10, y) == 0: is_port = True
			if mapper_q.get_terrain(the_line.the_world.cursor, x+10, y+10) == 0: is_port = True
		
		if not is_port:
			return order_block.fail(results, "%s that is not next to the sea" % str((x, y)))
	
	# Supply list
	supply_dict_raw = {}
	if supply_list_s != None:
		supply_list_s = supply_list_s.split(",")
		for s in supply_list_s:
			sls = s.lower().strip()
			if sls in cities_lookup:
				supply_dict_raw[cities_lookup[sls]] = 9999999
	else:
		for c in city_dict.keys():
			supply_dict_raw[c] = 9999999
	
	# print("")
	# print(supply_dict_raw)
	
	if debug:
		results['debug'].append("First pass:"******"City ID %d was not in city_dict" % c)
			return order_block.fail(results, "One or more of the cities used as a source were not valid (ID: %d)" % c)

		the_city = city_dict[c]
		if the_city.dead > 0: continue
		
		if new_city_continent != path_f.get_continent(the_line.the_world.cursor, (the_city.x, the_city.y)):
			if not is_port or not the_city.port:
				if len(supply_dict_raw) == 1:
					if debug:
						results['debug'].append("Continent of target: %s" % new_city_continent)
						results['debug'].append("Continent of supply: %s" % path_f.get_continent(the_line.the_world.cursor, (the_city.x, the_city.y)))
						
					if not the_city.port and not is_port:
						return order_block.fail(results, "the city you supplied is on another contintent and neither this city nor the new one are a port")
					elif not the_city.port:
						return order_block.fail(results, "the city you supplied is on another contintent and the existing city is not a port")
					elif not is_port:
						return order_block.fail(results, "the city you supplied is on another contintent and the new city is not a port")
					else:
						raise Exception("No handle")	
				
				if not is_port:
					if debug:
						results['debug'].append("Skipped %s due to the new city being on another landmass and not a port" % (city_dict[c].name))
				elif not the_city.port:
					if debug:
						results['debug'].append("Skipped %s due to it not being a port" % (city_dict[c].name))
				
				# We need both to be a port if they're on different landmasses
				continue
			
			path_data = path_f.path(the_line.the_world.cursor, [(the_city.x, the_city.y), (x, y)],
				move_speed="Sailing", move_type="Sail")
			supply_dict[c] = path_data.time_cost
			if debug:
				results['debug'].append("Added %s to dict with %s (sailing)" % (city_dict[c].name, int(path_data.time_cost)))
			
		else:# Same continent
			path_data = path_f.path(the_line.the_world.cursor, [(the_city.x, the_city.y), (x, y)],
				move_speed="Colonists", move_type="Colonists")
			supply_dict[c] = path_data.time_cost
			if debug:
				results['debug'].append("Added %s to dict with %s (walking)" % (city_dict[c].name, int(path_data.time_cost)))
		
		# Work out if it's in range
		if supply_dict[c] > 182:# 6 months
			if debug:
				results['debug'].append("Removed %s from dict with, it took %s" % (city_dict[c].name, int(supply_dict[c])))
			del(supply_dict[c])
	
	if debug:
		results['debug'].append("\nCities in range:")
		for k, v in supply_dict.items():
			results['debug'].append("%s has %s to offer (travel time %s)" % (city_dict[k].name, city_dict[k].population, int(v)))
		
		# results['debug'].append("")
	
	cities_changed = True
	while cities_changed:
		if len(supply_dict) < 1:
			if groups['city_list'] == None:
				return order_block.fail(results, "the cities able to reach the new location were too far away or not large enough")
			else:
				return order_block.fail(results, "the cities able to reach the new location from the list provided were too far away or not large enough")
		
		cities_to_delete	= []
		cities_changed		= False
		
		city_count			= len(supply_dict)
		amount_per_city		= math.ceil(size/city_count)
		
		for c in supply_dict.keys():
			# Check the city size
			if city_dict[c].population < amount_per_city:
				# if debug: print("deleted %s (%s) pop:%s < %s" % (c, city_dict[c].name, supply_dict[c], amount_per_city))
				cities_to_delete.append(c)
				cities_changed = True
		
		for c in cities_to_delete:
			del(supply_dict[c])
	
	# Get cost
	results['cost'] = res_dict.Res_dict("Materials:%s" % (size/1000))
	
	# Check affordability
	affordability = the_team.resources.affordable(results['cost'])[0]
	
	if not affordability and "Materials" not in the_team.overbudget:
		return order_block.fail_cost(results)
	
	#	EXECUTION
	#------------------------
	# Queries
	results['queries'].append("-- Founding %s at %s for team:%d" % (new_city_name, str((x, y)), the_team.id))
	if dead_city > 1: results['queries'].extend(city_f.make_remove_dead_city_query(dead_city))
	results['queries'].extend(city_f.make_founding_query(
		the_team.id, new_city_name, (x, y), is_port, is_nomadic, size, supply_dict.keys())
	)
	
	# Apply cost
	for c in supply_dict.keys():
		city_dict[c].population -= (size/len(supply_dict))
	
	the_team.resources -= results['cost'].discrete()
	
	# Result
	results['results'].append("%s was successfully founded at %s at a size of %s" % (new_city_name, str((x, y)), size))
	
	return order_block.success(results)
Beispiel #2
0
def migration_order(the_line, groups, debug=False):
	results = order_block.default_line_results(the_line)
	
	city_dict		= the_line.the_world.cities_from_team(the_line.block.team)
	cities_lookup	= the_line.the_world.cities_lookup(lower=True)
	
	# Can we find the city?
	if groups['city'].lower() not in cities_lookup:
		return order_block.fail(results, "there is no city by the name of '%s'" % groups['city'])
	else:
		the_city = city_dict[cities_lookup[groups['city'].lower()]]
	
	the_team = the_line.the_world.teams()[the_line.block.team]
	
	# Target
	try:
		target = (int(groups['x']), int(groups['y']))
	except Exception as e:
		return order_block.fail(results, "%s was not moved because trying to read the target location produced an error: %s" % (groups['city_name'], e.args[0]))
	
	# Default result
	results = order_block.default_line_results(the_line, "%s could not be moved to %s because" % (the_city.name, str(target)))
	
	# First we check it's on the same continent, if it's not you can't migrate there
	our_continent		= path_f.get_continent(the_line.the_world.cursor, (the_city.x, the_city.y))
	target_continent	= path_f.get_continent(the_line.the_world.cursor, target)
	
	# Before we path, lets check that they can walk there
	if our_continent != target_continent:
		if target_continent == None:# or target_continent == -1:
			return order_block.fail(results, "the target is the ocean")
		else:
			return order_block.fail(results, "the target is on a different island")
	
	terrain = mapper_q.get_terrain(the_line.the_world.cursor, target[0], target[1])
	if map_data.terrain[terrain] in ('lake', 'XYZ_1', 'XYZ_2', 'XYZ_3'):
		return order_block.fail(results, "you cannot migrate to that terrain (%s)" % map_data.terrain[terrain])
	
	# Pathing time!
	journey = path_f.path(
		cursor=the_line.the_world.cursor,
		waypoints=[(the_city.x, the_city.y), target],
		move_speed="Nomads",
		move_type="Nomads")
	
	# Does this take more than a year?
	travel_time = journey.time_cost
	actual_target = target
	
	current_time = 0
	if journey.time_cost > 365:
		for s in journey.steps:
			if current_time + s['time_cost'] <= 365:
				actual_target = s['tile']
				current_time += s['time_cost']
		
		travel_time = current_time
	
	if actual_target != target:
		results['results'].append("%s migrated to %s, to migrate to %s will take another %s days" % (the_city.name, str(actual_target), str(target), (journey.time_cost - travel_time)))
	else:
		results['results'].append("%s migrated to %s" % (the_city.name, str(target)))
	
	# Get the query to make it happen
	results['queries'].extend(city_f.make_migration_query(the_city.id, actual_target, travel_time))
	
	return order_block.success(results)
Beispiel #3
0
def build_distance_matrix(the_world, verbose=False):
	borders		= the_world.relations()
	city_dict	= the_world.live_cities()
	
	# print(borders)
	# exit()
	
	distance_matrix = {}
	city_contintent = {}
	
	func_start = time.time()
	
	i = 0
	paths = 0
	
	it = city_dict.items()
	if verbose:
		it = cli_f.progressbar(city_dict.items(), "Pathing:", 60, True)
	
	worst_land_path = (-1, -1, -1)
	worst_water_path = (-1, -1, -1)
	for i1, c1 in it:
		# i1 = 1224
		# c1 = city_dict[i1]
		
		started = time.time()
		links = 0
		
		i += 1
		
		if i1 not in city_contintent:
			city_contintent[i1] = path_f.get_continent(the_world.cursor, (c1.x, c1.y))
		
		for i2, c2 in city_dict.items():
			# i2 = 1280
			# c2 = city_dict[i2]
			
			# print()
			# print(city_contintent[i1])
			# print(path_f.get_continent(the_world.cursor, (c2.x, c2.y)))
			
			# A city can't trade with itself...
			if i2 == i1:
				continue
			
			# Check we've not already done this
			if (i1, i2) in distance_matrix:
				continue
			
			# Check borders
			# [Host][Visitor]
			# if borders.get[c2.team][c1.team] <= team.border_states.index("Closed"):
			if the_world.get_border(c2.team, c1.team) <= team.border_states.index("Closed"):
				continue
			
			# if borders[c1.team][c2.team] <= team.border_states.index("At war"):
			if the_world.get_border(c1.team, c2.team) <= team.border_states.index("At war"):
				continue
			
			# Get continent stuff
			if i2 not in city_contintent:
				city_contintent[i2] = path_f.get_continent(the_world.cursor, (c2.x, c2.y))
			
			# Reset distance
			dist = None
			crow_dist = path_f.pythagoras_cities(c1, c2)
			
			# If on same continent
			if city_contintent[i1] == city_contintent[i2] and crow_dist < sad_rules.max_crow_dist_land:
				if crow_dist <= sad_rules.min_trade_distance:
					dist = DeadPath(sad_rules.min_trade_distance)
				else:
					path_time = time.time()
					try:
						dist = path_f.path(the_world.cursor, [(c1.x, c1.y), (c2.x, c2.y)], move_speed="Merchant", move_type="Merchant", exception_in_timeout=True, timeout_limit=1000)
						paths += 1
					except Exception as e:
						print("Trying to path %s (%d) to %s (%d)" % (c1.name, i1, c2.name, i2))
						print("Continent %s to %s" % (city_contintent[i1], city_contintent[i2]))
						raise
					path_time = time.time() - path_time
					if path_time > worst_land_path[2]:
						worst_land_path = ((c1.x, c1.y), (c2.x, c2.y), path_time, "points={0}%2C+{1}%2C+{2}%2C+{3}&move_speed=Merchant&move_type=Merchant".format(
							c1.x, c1.y, c2.x, c2.y
						))
			
			# If both are ports then we can try the water
			if c1.port and c2.port and crow_dist < sad_rules.max_crow_dist_water:
				path_time = time.time()
				try:
					dist_sea = path_f.path(the_world.cursor, [(c1.x, c1.y), (c2.x, c2.y)], move_speed="Sailing", move_type="Sail", exception_in_timeout=True)
					paths += 1
				except Exception as e:
					print("Trying to path %s (%d) to %s (%d)" % (c1.name, i1, c2.name, i2))
					raise
				
				path_time = time.time() - path_time
				if path_time > worst_water_path[2]:
					worst_water_path = ((c1.x, c1.y), (c2.x, c2.y), path_time, "points={0}%2C+{1}%2C+{2}%2C+{3}&move_speed=Sailing&move_type=Sail".format(
						c1.x, c1.y, c2.x, c2.y
					))
				
				# Now pick the fastest
				if dist == None or dist.time_cost > dist_sea.time_cost:
					dist = dist_sea
			
			# Is it none?
			if dist == None:
				time_cost = 99999
			else:
				time_cost = sad_rules.trade_distance(dist.time_cost)
				links += 1
			
			# if borders[c2.team][c1.team] == team.border_states.index("Segregated"):
			if the_world.get_border(c2.team, c1.team) <= team.border_states.index("Segregated"):
				time_cost *= sad_rules.segregated_multiplier
			
			if time_cost > sad_rules.max_trade_travel_time: continue
			distance_matrix[(i1, i2)] = time_cost
			
			# If we have the same borders round the other way then we can skip this path later
			# if borders[c2.team][c1.team] == borders[c1.team][c2.team]:
			if the_world.get_border(c2.team, c1.team) == the_world.get_border(c1.team, c2.team):
				distance_matrix[(i2, i1)] = distance_matrix[(i1, i2)]
		
		# print("%d of %d in %ss (%d links)" % (i, len(city_dict), round(time.time() - started, 2), links))
		"""
		Origional
		Tried 103,505 paths
		Completed in 446 seconds
		
		After applying: "if (i1, i2) in distance_matrix" with same border checking
		Tried 79,979 paths
		Completed in 338 seconds
		
		After dropping the timeout exception from 10k to 1k
		Tried 79,979 paths
		Completed in 335 seconds
		
		After adding in the min distance (10)
		Tried 79,958 paths
		Completed in 337 seconds
		
		Adding in dead tiles and a smaller (10 not 1000) move cost
		Tried 79,958 paths
		Completed in 336 seconds
		"""
	
	# Some stats stuff
	if verbose:
		print("Worst land path: %s -> %s in %s = http://localhost/rob3/web.py?mode=path_map&%s" % worst_land_path)
		print("Worst water path: %s -> %s in %s = http://localhost/rob3/web.py?mode=path_map&%s" % worst_water_path)
		
		total_time = time.time() - func_start
		print("Tried %s paths in %s seconds, avg %ss per path or %s paths per second" % (format(paths, ','), int(total_time), round(total_time/paths, 3), round(paths/total_time, 2)))
	
	# Now to save it
	q = "INSERT INTO trade_distances (city_1, city_2, distance) values %s;" % ",".join(
		["(%d, %d, %d)" % (c[0], c[1], d) for c, d in distance_matrix.items()]
	)
	
	the_world.cursor.execute("DELETE FROM trade_distances")
	if verbose:
		print("Completed in %d seconds" % int(time.time() - func_start))
	
	the_world.cursor.execute(q)