def test_valid_puzzle(): puzzle = (utils.project_root() / 'tasks' / 'chain-puzzle-examples' / 'puzzle.cond').read_text() task = (utils.project_root() / 'tasks' / 'chain-puzzle-examples' / 'task.desc').read_text() result = puz(puzzle, task) assert result == 'ok', result
def test_valid_solution(): task = (utils.project_root() / 'tasks' / 'part-1-examples' / 'example-01.desc').read_text() sol = (utils.project_root() / 'tasks' / 'part-1-examples' / 'example-01-1.sol').read_text() result = run(task, sol) print(result) assert result.time == 48
def test_invalid_puzzle(): cond = (utils.project_root() / 'production' / 'golden' / 'puzzle.cond').read_text() desc = (utils.project_root() / 'production' / 'golden' / 'broken.desc').read_text() result = puz(cond, desc) print(result) assert result != 'ok'
def main(): t = int(time.time()) conn = db.get_conn() cur = conn.cursor() cur.execute(''' SELECT problems.name, traces.id, traces.energy FROM problems JOIN traces ON traces.problem_id = problems.id WHERE problems.name LIKE 'F%' AND traces.status = 'DONE' ''') rows = cur.fetchall() best_by_problem = {} for problem_name, trace_id, energy in rows: assert energy is not None k = (energy, trace_id) if problem_name not in best_by_problem: best_by_problem[problem_name] = k else: if k < best_by_problem[problem_name]: best_by_problem[problem_name] = k print(best_by_problem) with open(utils.project_root() / 'outputs' / f'submission_{t}.manifest', 'w') as fout: for problem_name, (score, trace_id) in sorted(best_by_problem.items()): fout.write(f'{problem_name} {score:>15} /trace/{trace_id}\n') path = (utils.project_root() / 'outputs' / f'submission_{t}.zip') z = zipfile.ZipFile(path, 'w') for problem_name, (score, trace_id) in sorted(best_by_problem.items()): print(problem_name) cur.execute('SELECT data FROM traces WHERE id = %s', [trace_id]) [data] = cur.fetchone() data = zlib.decompress(data) z.writestr(zipfile.ZipInfo(f'{problem_name}.nbt'), data) for name in data_files.full_names(): if name not in best_by_problem: print(name, 'from defaults') z.writestr(zipfile.ZipInfo(f'{name}.nbt'), data_files.full_default_trace(name)) z.close() with open(path, 'rb') as fin: h = hashlib.sha256(fin.read()).hexdigest() print(f'File: {path}') print(f'SHA256: {h}')
def main(): s = Path(utils.project_root() / 'tasks' / 'part-1-initial' / 'prob-002.desc').read_text() task = Task.parse(s) _, sol, _ = solve(task, '') sol = compose_actions(sol) print(sol) print(len(sol), 'time units') sol_path = Path(utils.project_root() / 'outputs' / 'example-01-rotator.sol') sol_path.write_text(sol) print('result saved to', sol_path)
def main(): s = Path(utils.project_root() / 'tasks' / 'part-1-examples' / 'example-01.desc').read_text() task = Task.parse(s) expected_score, sol, extra = solve(task) print('expected score', expected_score) sol = compose_actions(sol) print(sol) print(len(sol), 'time units') sol_path = Path(utils.project_root() / 'outputs' / 'example-01-greedy.sol') sol_path.write_text(sol) print('result saved to', sol_path)
def main(): puzzle = utils.project_root() / 'tasks' / 'chain-puzzle-examples' / 'puzzle.cond' puzzle_str = puzzle.read_text() puzzle = Puzzle.parse(puzzle_str) print(repr(puzzle)) # return print('solving...') task = solve(puzzle) (utils.project_root() / 'outputs' / 'my_task.desc').write_text(str(task)) result = validate.puz(puzzle_str, str(task)) print(result)
def main(): t = int(time.time()) conn = db.get_conn() cur = conn.cursor() cur.execute(''' SELECT tasks.name, solutions.id, solutions.score, solutions.scent FROM tasks JOIN solutions ON solutions.task_id = tasks.id WHERE solutions.status = 'DONE' AND tasks.name LIKE 'prob-%' ''') rows = cur.fetchall() best_by_task = {} for task_name, sol_id, score, scent in rows: assert score is not None k = (score, sol_id, scent) if task_name not in best_by_task: best_by_task[task_name] = k else: if k < best_by_task[task_name]: best_by_task[task_name] = k print(best_by_task) with open(utils.project_root() / 'outputs' / f'submission_{t}.manifest', 'w') as fout: for task_name, (score, sol_id, scent) in sorted(best_by_task.items()): fout.write(f'{task_name} {score:>15} /sol/{sol_id} by {scent}\n') path = (utils.project_root() / 'outputs' / f'submission_{t}.zip') z = zipfile.ZipFile(path, 'w') for task_name, (score, sol_id, scent) in sorted(best_by_task.items()): print(task_name) cur.execute('SELECT data FROM solutions WHERE id = %s', [sol_id]) [data] = cur.fetchone() data = zlib.decompress(data).decode() z.writestr(zipfile.ZipInfo(f'{task_name}.sol'), data) z.close() with open(path, 'rb') as fin: h = hashlib.sha256(fin.read()).hexdigest() print(f'File: {path}') print(f'SHA256: {h}')
def test_wheel_timer(): task_data = Path(utils.project_root() / 'tasks' / 'part-0-mock' / 'wheels.desc').read_text() task = GridTask(Task.parse(task_data)) actionlist = ('QQDD' + 'F' + 'ZZZZZZZZZZZZZZZZZZZZ' + 'F' + 'ZZZZZZZZZZZZZZZZZZZZ' + 'ZZZZZZZZZ' + 'ZZZZZZZZZZZZZZZZZZZZ' + 'ZZZZZZZZZZZZZZZZZZZZ' + 'ZZZZZZZZZZ' + 'D') game = Game(task) for a in actionlist: game.apply_action(Action.simple(a)) solution = compose_actions(game.get_actions()) expected_score = game.finished() assert expected_score is None game = Game(task) for a in actionlist: game.apply_action(Action.simple(a)) game.apply_action(Action.WSAD('D')) solution = compose_actions(game.get_actions()) expected_score = game.finished() er = validate.run(task_data, solution) assert er.time is not None assert er.time == expected_score
def test_invalid_solution(): task = (utils.project_root() / 'tasks' / 'part-1-examples' / 'example-01.desc').read_text() sol = 'WSAD' result = run(task, sol) print(result) assert result.time is None
def puz(cond: str, descr: str) -> str: '''Return 'ok' or error message.''' assert (utils.project_root() / 'production' / 'golden' / 'node_modules').exists(), ''' node_modules/ not found You probably need to run the following: cd production/golden npm install ''' cond_name = tempfile.NamedTemporaryFile(delete=False).name with open(cond_name, 'w') as fout: fout.write(cond) descr_name = tempfile.NamedTemporaryFile(delete=False).name with open(descr_name, 'w') as fout: fout.write(descr) fname = os.path.join(os.path.dirname(__file__), "run.js") result = subprocess.check_output( ("node", fname, '-c', cond_name, '-d', descr_name), universal_newlines=True) os.remove(cond_name) os.remove(descr_name) result = result.strip() if result == 'Success===null': return 'ok' assert result != 'ok' return result
def test_insect(): s = (utils.project_root() / 'tasks' / 'part-0-mock' / 'prob-2003.desc').read_text() solver = InsectSolver([]) result = solver.solve(s) print(result) vr = validate.run(s, result.data) print(vr)
def main(): s = Path(utils.project_root() / 'tasks' / 'part-1-initial' / 'prob-145.desc').read_text() solver = BoostySolver([]) t = time() sol = solver.solve(s) print(f'time elapsed: {time() - t:.4}') print(sol) task = Task.parse(s) score, sol, _ = solve(Game(GridTask(task))) print("score:", score) sol = compose_actions(sol) print(sol) print(len(sol), 'time units') sol_path = Path(utils.project_root() / 'outputs' / 'example-01-boosty.sol') sol_path.write_text(sol) print('result saved to', sol_path)
def run(map: str, solution: str, boosters=None) -> ValidatorResult: assert (utils.project_root() / 'production' / 'golden' / 'node_modules').exists(), ''' node_modules/ not found You probably need to run the following: cd production/golden npm install ''' map_name = tempfile.NamedTemporaryFile(delete=False).name with open(map_name, 'w') as fout: fout.write(map) solution_name = tempfile.NamedTemporaryFile(delete=False).name with open(solution_name, 'w') as fout: fout.write(solution) booster_name = None if boosters is not None: booster_name = tempfile.NamedTemporaryFile(delete=False).name with open(booster_name, 'w') as fout: fout.write(booster) fname = os.path.join(os.path.dirname(__file__), "run.js") cmd = ["node", fname, '-m', map_name, '-s', solution_name] if boosters is not None: cmd += ['-b', booster_name] while True: result = subprocess.check_output(cmd, universal_newlines=True) result = result.strip() # This list was produced by running # SELECT id, extra->'validator'->'error', scent FROM solutions WHERE status='CHECK_FAIL' # and checking what looks like bullshit if result in [ '', 'Done uploading solution', 'Done uploading task description', 'Cannot check: some parts of the input are missing or malformed', ]: logging.warning( f'Retrying what looks like chrome flake ({result})') continue break m = re.match(r'Success===(\d+)$', result) if m: result = ValidatorResult(time=int(m.group(1)), extra={}) else: result = ValidatorResult(time=None, extra=dict(error=result)) os.remove(map_name) os.remove(solution_name) if boosters is not None: os.remove(booster_name) return result
def main(): problem = 11 sol = GreedyBeamSolver([]).solve(utils.get_problem_raw(problem)) print(sol.extra) sol = sol.data print(len(sol), 'time units') sol_path = Path(utils.project_root() / 'outputs' / f'beam-{problem}.sol') sol_path.write_text(sol) print('result saved to', sol_path)
def run_for_errors(actions: List[Action]): task_data = Path(utils.project_root() / 'tasks' / 'part-0-mock' / 'prob-2003.desc').read_text() task = GridTask(Task.parse(task_data)) game = Game(task) try: for a in actions: game.apply_action(a) except InvalidActionException: return else: assert False
def main(): # s = utils.get_problem_raw(1) s = Path(utils.project_root() / 'tasks' / 'part-0-mock' / 'prob-2003.desc').read_text() solver = InsectSolver([]) result = solver.solve(s) logging.info(result) logging.info('validating...') vr = validate.run(s, result.data) logging.info(vr)
def main(): s = Path(utils.project_root() / 'tasks' / 'part-2-teleports' / 'prob-213.desc').read_text() args_options = (['squared', 'drill', 'teleport'], ['squared', 'no-drill', 'teleport'], ['squared', 'drill'], ['squared', 'no-drill']) bestscore = None bestoption = None for arg in args_options: tweaker = TweakerSolver(arg) sol = tweaker.solve(s) print(sol.expected_score, 'time units') if bestscore is None or bestscore > sol.expected_score: bestscore, bestoption = sol.expected_score, arg print('\n: best option was ', bestoption, '\n') sol_path = Path(utils.project_root() / 'outputs' / 'prob-ttt-tweaker.sol') sol_path.write_text(sol.data) print('result saved to', sol_path)
def main(): logging.basicConfig( level=logging.INFO, format='%(levelname).1s %(module)10.10s:%(lineno)-4d %(message)s') conn = db.get_conn() cur = conn.cursor() legends = {} for part_number, part_name in (1, 'initial'), (2, 'teleports'), (3, 'clones'): with ZipFile(utils.project_root() / 'tasks' / f'part-{part_number}-{part_name}.zip') as z: with z.open(f'part-{part_number}-legend.txt') as fin: for line in fin: name, legend = line.decode().split(' - ', maxsplit=1) legends[name] = legend.rstrip() for filename in z.namelist(): if filename.endswith('-legend.txt'): continue m = re.match(r'(prob-\d{3}).desc$', filename) assert m, filename name = m.group(1) with z.open(filename, 'r') as fin: task = fin.read().decode() data = zlib.compress(task.encode()) extra = dict(legend=legends[name]) cur.execute( ''' INSERT INTO tasks( name, data, extra, invocation_id, time) VALUES (%s, %s, %s, %s, NOW()) ON CONFLICT DO NOTHING RETURNING id ''', [ name, data, json.dumps(extra), db.get_this_invocation_id(conn) ]) res = cur.fetchall() if res: [[task_id]] = res logger.info(f'Uploaded {name!r} as /task/{task_id}') else: logger.info(f'Task {name!r} already exists') db.record_this_invocation(conn, status=db.Stopped()) conn.commit()
def test_run_from_file(): modelfile = utils.project_root() / 'julie_scratch' / 'LA014_tgt.mdl' tracefile = utils.project_root() / 'julie_scratch' / 'LA014_dflt.nbt' logfilename = str(utils.project_root() / 'outputs' / 'cpp_emulator.log') mf = open(modelfile, 'rb') m = Cpp.Matrix.parse(mf.read()) mf.close() em = Cpp.Emulator(None, m) # (source, target) errmsg = "" tf = open(tracefile, 'rb') cmds = commands.parse_commands(tf.read(), errmsg) tf.close() commandlist = list(map(cppm.cmd_to_cpp, cmds)) em.set_trace(commandlist) em.setlogfile(logfilename) em.run() assert em.energy() == 501700108
def run_one_bot_game(actions: List[Action]): task_data = Path(utils.project_root() / 'tasks' / 'part-0-mock' / 'prob-2003.desc').read_text() task = GridTask(Task.parse(task_data)) game = Game(task) for a in actions: game.apply_action(a) solution = compose_actions(game.get_actions()) expected_score = game.finished() er = validate.run(task_data, solution) assert er.time is not None assert er.time == expected_score
def main_run_file(): from production import utils modelfile = utils.project_root() / 'julie_scratch' / 'LA014_tgt.mdl' tracefile = utils.project_root() / 'julie_scratch' / 'LA014_dflt.nbt' logfile = str(utils.project_root() / 'outputs' / 'cpp_emulator.log') mf = open(modelfile, 'rb') m = Cpp.Matrix.parse(mf.read()) mf.close() em = Cpp.Emulator(None, m) # (source, target) error = "" tf = open(tracefile, 'rb') cmds = commands.parse_commands(tf.read(), error) tf.close() cmdlist = list(map(cmd_to_cpp, cmds)) em.set_trace(cmdlist) em.setlogfile(logfile) em.run() print("Energy: ", em.energy())
def run_full(src_model_data: Optional[bytes], tgt_model_data: Optional[bytes], trace_data: bytes) -> EmulatorResult: em = Cpp.Emulator() if src_model_data: em.set_model(src_model_data, 's') if tgt_model_data: em.set_model(tgt_model_data, 's') em.set_trace(trace_data) em.setlogfile(str(utils.project_root() / 'outputs' / 'cpp_emulator.log')) try: em.run() except Cpp.SimulatorException as e: return EmulatorResult(energy=None, extra={'error message': str(e)}) return EmulatorResult(energy=em.energy(), extra={})
def main(): conn = db.get_conn() while True: block = lambda_chain.get_block_info() upload_current_task(conn, block) f = utils.project_root() / 'outputs' / f'block-{block.number:04d}.cond' f.write_text(block.puzzle) logging.info(f'Block puzzle saved to {f}') try: task = puzzle_solver.solve(Puzzle.parse(block.puzzle)) task = str(task) logging.info(f'Validating puzzle solution...') result = validate.puz(block.puzzle, task) logging.info(result) assert result == 'ok' except KeyboardInterrupt: raise except: traceback.print_exc() db.record_this_invocation(conn, status=db.KeepRunning(60)) conn.commit() logging.info('waiting and retrying...') time.sleep(20) continue block_submitted = False while True: new_block = lambda_chain.get_block_info() logging.info(f'block age: {int(new_block.age_in_seconds)}s') if new_block.number != block.number: logging.info('new block appeared ' + '-' * 30) break if not block_submitted: sol = find_best_solution(conn, f'block-{block.number:04d}') if sol is not None and new_block.age_in_seconds > 850: lambda_chain.submit(block.number, solution=sol, task=task) block_submitted = True logging.info('waiting...') db.record_this_invocation(conn, status=db.KeepRunning(60)) conn.commit() time.sleep(20)
def run_cloned_game(actions: List[List[Action]]): task_data = Path(utils.project_root() / 'tasks' / 'part-0-mock' / 'prob-2003.desc').read_text() task = GridTask(Task.parse(task_data)) game = Game(task) indices = [0] * len(actions) current = 0 while not game.finished(): if current == 0: botcount = len(game.bots) i = indices[current] game.apply_action(actions[current][i], current) indices[current] += 1 current = (current + 1) % botcount solution = compose_actions(game.get_actions()) expected_score = game.finished() er = validate.run(task_data, solution) assert er.time is not None assert er.time == expected_score
def main(): args = parse_args() conn = db.get_conn() cur = conn.cursor() solver = ALL_SOLVERS[args.solver](args.solver_args) logger.info(f'Solver scent: {solver.scent()!r}') seen_tasks = set() def get_tasks(): # Select tasks that don't have solutions with our scent. cur.execute( ''' SELECT tasks.id FROM tasks LEFT OUTER JOIN ( SELECT task_id AS solution_task_id FROM solutions WHERE scent = %s ) AS my_solutions ON solution_task_id = tasks.id WHERE solution_task_id IS NULL AND NOT tasks.obsolete AND tasks.name LIKE %s ''', [solver.scent(), 'block-%' if args.only_chain else '%']) task_ids = [id for [id] in cur if id not in seen_tasks] seen_tasks.update(task_ids) logging.info(f'{len(task_ids)} tasks to solve: {task_ids}') # to reduce collisions when multiple solvers are working in parallel random.shuffle(task_ids) # task_ids.sort(reverse=True) return task_ids task_ids = get_tasks() num_workers = args.jobs output_queue = multiprocessing.Queue() input_queues = [multiprocessing.Queue() for _ in range(num_workers)] workers = [] for i, iq in enumerate(input_queues): log_path = utils.project_root( ) / 'outputs' / f'solver_worker_{i:02}.log' logging.info(f'Worker logging to {log_path}') w = multiprocessing.Process(target=work, args=(i, log_path, iq, output_queue)) w.start() available_workers = set(range(num_workers)) cur = conn.cursor() while True: while available_workers and task_ids: task_id = task_ids.pop() cur.execute( 'SELECT COUNT(*) FROM solutions WHERE task_id = %s AND scent = %s', [task_id, solver.scent()]) [num_attempts] = cur.fetchone() if num_attempts == 0: cur.execute('SELECT name, data FROM tasks WHERE id = %s', [task_id]) [task_name, task_data] = cur.fetchone() task_data = zlib.decompress(task_data).decode() worker_index = available_workers.pop() input_queues[worker_index].put( InputEntry(solver=solver, task_id=task_id, task_name=task_name, task_data=task_data)) logging.info(f'task/{task_id} goes to worker {worker_index}') else: logging.info( f'task/{task_id} already done by another worker, skipping') logging.info(f'{len(task_ids)} tasks remaining') #if len(available_workers) == num_workers: # break cont = False while True: if available_workers and not task_ids: logging.info( f'No more new tasks, {num_workers - len(available_workers)} still in progress, looking for more...' ) task_ids = get_tasks() if task_ids: cont = True break db.record_this_invocation( conn, status=db.KeepRunning(40), extra=dict(remaining_tasks=len(task_ids) + num_workers - len(available_workers))) conn.commit() try: output_entry = output_queue.get(timeout=20) break except queue.Empty: logging.info('waiting...') if cont: continue assert output_entry.worker_index not in available_workers available_workers.add(output_entry.worker_index) logging.info(f'Got solution for task/{output_entry.task_id}, ' f'score={output_entry.result.score} ' f'from worker {output_entry.worker_index}') if args.dry_run: logging.info(f'Skip saving because dry-run') else: put_solution(conn, output_entry.task_id, output_entry.result) conn.commit() db.record_this_invocation(conn, status=db.Stopped()) conn.commit() logging.info('All done, joining workers...') for iq in input_queues: iq.put(None) for w in workers: join()
def main_run_interactive(): import production.utils as utils # assuming we have left state and expecting right state # x-> # z ... ... ... | 2.. ... ... # | 2o. ... ... | .o. .o. ... # v ... ... ... | 3.. ... ... # y ----------> m = Model(3) m[Pos(1, 0, 1)] = 1 s = State(3) s.matrix = m s.harmonics = LOW s.energy = 30 s.bots = [Bot(bid=2, pos=Pos(0, 0, 1), seeds=[3, 4, 5])] cmds = [ commands.Fill(Diff(1, 1, 0)), commands.Fission(Diff(0, 0, 1), 2), commands.SMove(Diff(0, 0, -1)), commands.Wait() ] # pass state to emulator (step count set to 0) em = Cpp.Emulator(state_to_cpp(s)) # LOGGING -- temporary out of service # if no logfile name given, emulator doesn't log # problemname & solutionname are optional from production import utils em.setlogfile(str(utils.project_root() / 'outputs' / 'cpp_emulator.log')) em.setproblemname("some handmade problem") em.setsolutionname("John Doe's ingenious alg") # OPTION 1: Run set of commands cpp_cmds = list(map(cmd_to_cpp, cmds)) em.run_commands(cpp_cmds) cs = em.get_state() print("Energy: ", cs.energy) print("Central cell: ", cs[Cpp.Pos(1, 1, 1)]) print("Active bots: ", sum(b.active for b in cs.bots)) # OPTION 2: Command by command # x-> # z 2.. ... ... # | .o. .o. ... # v 3.. ... ... # y ----------> ccmd = cmd_to_cpp(commands.LMove(Diff(1, 0, 0), Diff(0, 0, 1))) msg = em.check_command(ccmd) print(msg == '', 'Error: ', msg) # void string if command is valid ccmd = cmd_to_cpp(commands.Fill(Diff(0, 0, 1))) msg = em.check_command(ccmd) print(msg == '', 'Error: ', msg) em.add_command(ccmd) ccmd = cmd_to_cpp(commands.SMove(Diff(0, 0, -1))) msg = em.check_command(ccmd) print(msg == '', 'Error: ', msg) # to check command and add iff it's valid ccmd = cmd_to_cpp(commands.Wait()) msg = em.check_add_command(ccmd) print(msg == '', 'Error: ', msg) # if there are enough commands for next step, new commands cannot # be checked until change of state, and every check will fail ccmd = cmd_to_cpp(commands.Wait()) msg = em.check_add_command(ccmd) print(msg == '', 'Error: ', msg) # you can still add command without checks print('Trace is full: ', em.steptrace_is_complete()) print('Energy: ', em.energy()) em.add_command(ccmd) em.run_step() print('Trace is full: ', em.steptrace_is_complete()) print('Energy: ', em.energy())
def _open_datazipfile(name): if name not in _zipfiles: _zipfiles[name] = ZipFile(utils.project_root() / 'data' / f'{name}.zip') return _zipfiles[name]
if __name__ == '__main__': logging.basicConfig( level=logging.INFO, format='%(levelname).1s %(module)10.10s:%(lineno)-4d %(message)s') logger = logging.getLogger(__name__) import time import cProfile import subprocess from production import utils from production.data_formats import * from production.solvers.rotator import RotatorSolver if __name__ == '__main__': profile_path = utils.project_root() / 'outputs' / 'profile' callgraph_path = utils.project_root() / 'outputs' / 'callgraph.png' task = utils.get_problem_raw(100) solver = RotatorSolver([]) start = time.time() cProfile.run('solver.solve(task)', profile_path) logging.info(f'it took {time.time() - start}s') subprocess.check_call( f'gprof2dot -f pstats -n 2 {profile_path} | dot -Tpng -o {callgraph_path}', shell=True) print(f'see {callgraph_path}')
def main(): logging.basicConfig( level=logging.INFO, format='%(levelname).1s %(module)10.10s:%(lineno)-4d %(message)s') conn = db.get_conn() attempts_by_car_id = get_attempts(conn) def want_solve(attempts): return not attempts car_ids = [ car_id for car_id, attempts in attempts_by_car_id.items() if want_solve(attempts)] logging.info(f'Found {len(car_ids)} cars to solve') # to reduce collisions when multiple solvers are working in parallel random.shuffle(car_ids) num_workers = multiprocessing.cpu_count() output_queue = multiprocessing.SimpleQueue() input_queues = [multiprocessing.SimpleQueue() for _ in range(num_workers)] workers = [] for i, iq in enumerate(input_queues): log_path = utils.project_root() / 'outputs' / f'solver_worker_{i:02}.log' logging.info(f'Worker logging to {log_path}') w = multiprocessing.Process(target=work, args=(i, log_path, iq, output_queue)) w.start() available_workers = set(range(num_workers)) while True: if available_workers and car_ids: car_id = car_ids.pop() attempts = get_attempts(conn, car_id=car_id)[car_id] if want_solve(attempts): cur = conn.cursor() cur.execute('SELECT data FROM cars WHERE id = %s', [car_id]) [car_data] = cur.fetchone() worker_index = available_workers.pop() input_queues[worker_index].put(InputEntry(car_id=car_id, car_data=car_data)) logging.info(f'car/{car_id} goes to worker {worker_index}') else: logging.info(f'Skipping car/{car_id}') else: if len(available_workers) == num_workers: break output_entry = output_queue.get() assert output_entry.worker_index not in available_workers available_workers.add(output_entry.worker_index) logging.info( f'Got fuel for car/{output_entry.car_id}, ' f'score={output_entry.result.score} ' f'from worker {output_entry.worker_index}') put_fuel(conn, output_entry.car_id, output_entry.result) conn.commit() logging.info('All done, joining workers...') for iq in input_queues: iq.put(None) for w in workers: join()