Add existing .py files

This commit is contained in:
Gabriel Augendre 2017-08-10 21:31:16 +02:00
commit 309a46c778
No known key found for this signature in database
GPG key ID: F360212F958357D4
15 changed files with 1131 additions and 0 deletions

193
.gitignore vendored Normal file
View file

@ -0,0 +1,193 @@
# Created by https://www.gitignore.io/api/python,pycharm,osx
### OSX ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### PyCharm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### PyCharm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# End of https://www.gitignore.io/api/python,pycharm,osx

198
bender.py Normal file
View file

@ -0,0 +1,198 @@
import sys
import math
from enum import Enum
class Coordinate:
def __init__(self, r, c):
self.r = r
self.c = c
def __str__(self):
return str((self.r, self.c))
class Direction(Enum):
SOUTH = 1
EAST = 2
NORTH = 3
WEST = 4
class Game:
def __init__(self, city_map):
self.city_map = city_map
self.bender = Bender(self.city_map)
def run(self):
while self.bender.alive:
self.bender.step()
class Bender:
def __init__(self, city_map, position=None):
self.direction = Direction.SOUTH
self._position = position
self.fury = False
self.alive = True
self.city_map = city_map
self.invert = False
self.instructions = []
if not self.position:
for i, r in enumerate(self.city_map):
if '@' in r:
r, c = i, ''.join(r).find('@')
self.position = Coordinate(r, c)
@property
def position(self):
return self._position
@position.setter
def position(self, value):
self._position = value
# self.print_map()
def print_map(self, file=sys.stderr):
for r, row in enumerate(self.city_map):
if r != self.position.r:
line = ''.join(row)
else:
line = ''.join(row[:self.position.c]) + '*' + ''.join(row[self.position.c+1:])
print(line, file=file)
def step(self):
next_item = self.get_next_item()
if next_item == '#' or (next_item == 'X' and not self.fury):
self.bypass_obstacle()
self.step()
else:
self.step_forward()
if next_item == 'X' and self.fury:
self.city_map[self.position.r][self.position.c] = ' '
elif next_item == 'B':
self.fury = not self.fury
elif next_item == 'T':
self.beam_me_up()
elif next_item == 'I':
self.invert = not self.invert
elif next_item == 'S':
self.direction = Direction.SOUTH
elif next_item == 'E':
self.direction = Direction.EAST
elif next_item == 'N':
self.direction = Direction.NORTH
elif next_item == 'W':
self.direction = Direction.WEST
elif next_item == '$':
self.alive = False
for instruction in self.instructions:
print(instruction)
self.check_loop()
def check_loop(self):
if len(self.instructions) > 1000:
print('LOOP')
self.alive = False
def beam_me_up(self):
b_r, b_c = self.position.r, self.position.c
for i, row in enumerate(self.city_map):
if 'T' in row:
t_r, t_c = i, ''.join(row).find('T')
if t_r != b_r or t_c != b_c:
self.position = Coordinate(t_r, t_c)
return
def bypass_obstacle(self):
if not self.invert:
self.direction = Direction.SOUTH
if not self.is_obstacle(self.get_next_item()):
return
self.direction = Direction.EAST
if not self.is_obstacle(self.get_next_item()):
return
self.direction = Direction.NORTH
if not self.is_obstacle(self.get_next_item()):
return
self.direction = Direction.WEST
if not self.is_obstacle(self.get_next_item()):
return
else:
self.direction = Direction.WEST
if not self.is_obstacle(self.get_next_item()):
return
self.direction = Direction.NORTH
if not self.is_obstacle(self.get_next_item()):
return
self.direction = Direction.EAST
if not self.is_obstacle(self.get_next_item()):
return
self.direction = Direction.SOUTH
if not self.is_obstacle(self.get_next_item()):
return
def step_forward(self):
if self.direction == Direction.SOUTH:
self.position.r += 1
self.instructions.append('SOUTH')
elif self.direction == Direction.NORTH:
self.position.r -= 1
self.instructions.append('NORTH')
elif self.direction == Direction.EAST:
self.position.c += 1
self.instructions.append('EAST')
elif self.direction == Direction.WEST:
self.position.c -= 1
self.instructions.append('WEST')
# self.print_map()
def get_next_item(self):
if self.direction == Direction.SOUTH:
return self.city_map[self.position.r + 1][self.position.c]
elif self.direction == Direction.NORTH:
return self.city_map[self.position.r - 1][self.position.c]
elif self.direction == Direction.EAST:
return self.city_map[self.position.r][self.position.c + 1]
elif self.direction == Direction.WEST:
return self.city_map[self.position.r][self.position.c - 1]
def is_obstacle(self, next_item):
if self.fury:
return next_item == '#'
else:
return next_item == '#' or next_item == 'X'
def main():
l, c = [int(i) for i in input().split()]
city_map = []
for i in range(l):
city_map.append(list(input()))
game = Game(city_map)
game.run()
if __name__ == '__main__':
main()

42
cgx_formatter.py Normal file
View file

@ -0,0 +1,42 @@
import sys
import re
class Element:
pass
class Primitive(Element):
def __init__(self, value):
self.value = value
class Block(Element):
def __init__(self, lst=None):
if lst is None:
lst = []
self.content = lst
class KeyValue(Element):
def __init__(self, key, value):
self.key = key
self.value = value
def parse(cgx_input):
return cgx_input
def main():
n = int(input())
cgx_input = ''
for i in range(n):
cgx_input += input().strip()
output = parse(cgx_input)
print(output)
if __name__ == '__main__':
main()

45
community_graph.py Normal file
View file

@ -0,0 +1,45 @@
def main():
points = {}
min_abs, max_abs = 0, 0
min_ord, max_ord = 0, 0
n = int(input())
for i in range(n):
x, y = [int(j) for j in input().split()]
points[str_point(x, y)] = True
if x > max_abs:
max_abs = x
elif x < min_abs:
min_abs = x
if y > max_ord:
max_ord = y
elif y < min_ord:
min_ord = y
print_graph(points, min_abs, max_abs, min_ord, max_ord)
def print_graph(points, min_abs, max_abs, min_ord, max_ord):
for row in range(max_ord + 1, min_ord - 2, -1):
for col in range(min_abs - 1, max_abs + 2):
if points.get(str_point(col, row)):
print('*', end='')
elif row == 0 and col == 0:
print('+', end='')
elif row == 0:
print('-', end='')
elif col == 0:
print('|', end='')
else:
print('.', end='')
print()
def str_point(x, y):
return str(x) + ';' + str(y)
if __name__ == '__main__':
main()

42
community_speed_limit.py Normal file
View file

@ -0,0 +1,42 @@
import sys
from collections import OrderedDict
def main():
speed_limit = int(input())
n = int(input())
vehicles = OrderedDict()
for i in range(n):
plaque, distance, timestamp = input().split()
distance = int(distance)
timestamp = int(timestamp)
vehicles.setdefault(plaque, []).append((distance, timestamp))
found_excess = False
for k, v in vehicles.items():
overspeeds = find_over_speed(v, speed_limit)
for overspeed in overspeeds:
found_excess = True
print(k, overspeed[1])
if not found_excess:
print('OK')
def find_over_speed(vehicle, speed_limit):
distance, timestamp = vehicle[0]
overspeeds = []
for data in vehicle[1:]:
dist_delta = data[0] - distance
time_delta = (data[1] - timestamp) / 3600
speed = dist_delta / time_delta
if speed > speed_limit:
overspeeds.append((speed, data[0]))
distance, timestamp = data
return overspeeds
if __name__ == '__main__':
main()

55
dont_panic_2.py Normal file
View file

@ -0,0 +1,55 @@
import sys
import math
from collections import defaultdict
# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.
# nb_floors: number of floors
# width: width of the area
# nb_rounds: maximum number of rounds
# exit_floor: floor on which the exit is found
# exit_pos: position of the exit on its floor
# nb_total_clones: number of generated clones
# nb_additional_elevators: ignore (always zero)
# nb_elevators: number of elevators
nb_floors, width, nb_rounds, exit_floor, exit_pos, nb_total_clones, nb_additional_elevators, nb_elevators = [int(i) for
i in
input().split()]
elevators = defaultdict(int)
for i in range(nb_elevators):
# elevator_floor: floor on which this elevator is found
# elevator_pos: position of the elevator on its floor
elevator_floor, elevator_pos = [int(j) for j in input().split()]
elevators[elevator_floor] = elevator_pos
print(elevators, file=sys.stderr)
# game loop
while True:
# clone_floor: floor of the leading clone
# clone_pos: position of the leading clone on its floor
# direction: direction of the leading clone: LEFT or RIGHT
clone_floor, clone_pos, direction = input().split()
clone_floor = int(clone_floor)
clone_pos = int(clone_pos)
action = "WAIT"
if clone_pos == width - 1 or clone_pos == 0:
action = 'BLOCK'
elif clone_floor == exit_floor:
if clone_pos > exit_pos and direction == 'RIGHT':
action = 'BLOCK'
elif clone_pos < exit_pos and direction == 'LEFT':
action = 'BLOCK'
elif clone_pos > elevators[clone_floor] and direction == 'RIGHT':
action = 'BLOCK'
elif clone_pos < elevators[clone_floor] and direction == 'LEFT':
action = 'BLOCK'
print(action)
# Write an action using print
# To debug: print("Debug messages...", file=sys.stderr)
# action: WAIT or BLOCK

View file

@ -0,0 +1,21 @@
def main():
n = int(input()) # the number of relationships of influence
influences = {}
for i in range(n):
# x: a relationship of influence between two people (x influences y)
x, y = [int(j) for j in input().split()]
influences.setdefault(x, []).append(y)
all_lengths = [dfs(influences, x) for x in influences]
print(max(all_lengths))
def dfs(graph, node, depth=1):
for succ in graph[node]:
if succ in graph:
return dfs(graph, succ, depth + 1)
return depth + 1
if __name__ == '__main__':
main()

22
fizzbuzz.py Normal file
View file

@ -0,0 +1,22 @@
from collections import OrderedDict
def main():
mapping = OrderedDict({
3: 'Fizz',
5: 'Buzz'
})
for i in range(1, 101):
s = ''
for k, v in mapping.items():
if i % k == 0:
s += v
if s == '':
s = i
print(s)
if __name__ == '__main__':
main()

59
mars_lander_2.py Normal file
View file

@ -0,0 +1,59 @@
import sys
import math
# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.
# the number of points used to draw the surface of Mars.
surface_n = int(input())
prev_x, prev_y = (-1, -1)
start_x, start_y, end_x, end_y = (-1, -1, -1, -1)
for i in range(surface_n):
# land_x: X coordinate of a surface point. (0 to 6999)
# land_y: Y coordinate of a surface point.
# By linking all the points together in a sequential fashion, you form the surface of Mars.
land_x, land_y = [int(j) for j in input().split()]
if land_y == prev_y:
start_x, start_y = prev_x, prev_y
if start_x != -1 and land_y != prev_y:
end_x, end_y = prev_x, prev_y
prev_x, prev_y = land_x, land_y
# game loop
while True:
# h_speed: the horizontal speed (in m/s), can be negative.
# v_speed: the vertical speed (in m/s), can be negative.
# fuel: the quantity of remaining fuel in liters.
# rotate: the rotation angle in degrees (-90 to 90).
# power: the thrust power (0 to 4).
x, y, h_speed, v_speed, fuel, rotate, power = [int(i) for i in input().split()]
if x < start_x:
s_angle = -30
s_power = 4
elif x > end_x:
s_angle = 30
s_power = 4
else:
if h_speed > 10:
s_angle = 22
s_power = 4
elif h_speed < -10:
s_angle = -22
s_power = 4
else:
if y > end_y + 500:
s_power = 3
else:
s_power = 4
s_angle = 0
# rotate power. rotate is the desired rotation angle. power is the desired thrust power.
print("{} {}".format(s_angle, s_power))

19
network_cabling.py Normal file
View file

@ -0,0 +1,19 @@
import sys
import math
import statistics
n = int(input())
verticals = []
horizontals = []
for i in range(n):
x, y = [int(j) for j in input().split()]
horizontals.append(x)
verticals.append(y)
median = round(statistics.median(verticals))
total = max(horizontals) - min(horizontals)
for y in verticals:
total += abs(y - median)
print(total)

43
polynomial.py Normal file
View file

@ -0,0 +1,43 @@
from itertools import zip_longest
class Polynomial:
def __init__(self, *args):
if len(args) == 0:
args = (0,)
self.coeffs = args
def __repr__(self):
return 'Polynomial{}'.format(repr(self.coeffs))
def __str__(self):
xs = ('{}*x^{}'.format(self.coeffs[i], i) for i in range(len(self.coeffs)))
return ' + '.join(xs)
def __len__(self):
return len(self.coeffs) - 1
def __add__(self, other):
return Polynomial(*(x + y for x, y in zip_longest(self.coeffs, other.coeffs, fillvalue=0)))
def __sub__(self, other):
return Polynomial(*(x - y for x, y in zip_longest(self.coeffs, other.coeffs, fillvalue=0)))
def main():
p1 = Polynomial(1, 2, 3)
print(p1)
print(len(p1))
p2 = Polynomial(2, 3, 4, 5)
print(repr(p2))
print(p2)
print(len(p2))
print(p1 + p2)
print(p1 - p2)
p3 = Polynomial()
print(repr(p3))
p4 = Polynomial(0)
print(p4)
if __name__ == '__main__':
main()

20
super_computer.py Normal file
View file

@ -0,0 +1,20 @@
task_number = int(input())
calculus = []
for i in range(task_number):
start, duration = [int(j) for j in input().split(' ')]
calculus.append({
'start': start,
'end': start + duration - 1,
})
calculus.sort(key=lambda x: x['end'])
selected = 1
last_selected = calculus[0]
for calc in calculus[1:]:
if calc['start'] > last_selected['end']:
selected += 1
last_selected = calc
print(selected)

52
teads.py Normal file
View file

@ -0,0 +1,52 @@
import sys
import math
def main():
n = int(input())
people = {}
for i in range(n):
xi, yi = [int(j) for j in input().split()]
pxi = people.setdefault(xi, set())
pyi = people.setdefault(yi, set())
pxi.add(yi)
pyi.add(xi)
# perr('people', people)
min_length = math.inf
# perr('people', people)
for start in people:
res = {}
dfs3(people, start, res)
# perr('res', res)
inner_max_length = max(res.values())
if inner_max_length < min_length:
min_length = inner_max_length
print(min_length)
def dfs3(graph: dict, start: str, res: dict, depth: int = 0, visited: set = None) -> None:
if visited is None:
visited = set()
visited.add(start)
depth += 1
nxt = set(graph.get(start, [])) - visited
for succ in nxt:
distance = res.get(succ)
if (distance and depth < distance) or not distance:
res[succ] = depth
dfs3(graph, succ, res, depth, visited)
def perr(*values):
print(*values, file=sys.stderr)
if __name__ == '__main__':
main()

33
the_gift.py Normal file
View file

@ -0,0 +1,33 @@
import sys
import math
def main():
oods_number = int(input())
gift_cost = int(input())
budgets = []
for i in range(oods_number):
budgets.append(int(input()))
budgets.sort()
total = sum(budgets)
if total < gift_cost:
print("IMPOSSIBLE")
return
results = []
for i, budget in enumerate(budgets):
ideal = int((gift_cost - sum(results)) / (oods_number - i))
if budget < ideal:
results.append(budget)
else:
results.append(ideal)
results.sort()
for res in results:
print(res)
if __name__ == '__main__':
main()

287
the_labyrinth.py Normal file
View file

@ -0,0 +1,287 @@
import sys
import math
from collections import deque
WALL = '#'
EMPTY = '.'
FOG = '?'
COMMAND = 'C'
START = 'T'
LEFT = 'LEFT'
RIGHT = 'RIGHT'
UP = 'UP'
DOWN = 'DOWN'
TARGET_NODE = {
COMMAND: None,
START: None,
'explore': None
}
class Node:
def __init__(self, r, c, g=0, h=0, parent=None, maze=None):
if maze is None:
maze = [[]]
self._maze = maze
self.r = r
self.c = c
self.g = g
self.h = h
self.parent = parent
self.colored = False
def __str__(self):
return 'Node({}, {}, {})'.format(self.r, self.c, self.type)
def __repr__(self):
return self.__str__()
@property
def f(self):
return self.g + self.h
def __lt__(self, other):
return self.f < other.f
def __le__(self, other):
return self.f <= other.f
def __gt__(self, other):
return self.f > other.f
def __ge__(self, other):
return self.f >= other.f
def __eq__(self, other):
return self.r == other.r and self.c == other.c
@property
def type(self):
return self._maze[self.r][self.c]
def direction_to(self, other):
if other.r > self.r:
return DOWN
elif other.r < self.r:
return UP
elif other.c > self.c:
return RIGHT
elif other.c < self.c:
return LEFT
def distance_2_to(self, other):
return (self.c - other.c) ** 2 + (self.r - other.r) ** 2
def main():
# r: number of rows.
# c: number of columns.
# a: number of rounds between the time the alarm countdown is activated and the time the alarm goes off.
rows, columns, a = [int(i) for i in input().split()]
target = COMMAND
# visited = []
# current_node = None
# last_intersection = None
steps_remaining = 0
while True:
# kr: row where Kirk is located.
# kc: column where Kirk is located.
kr, kc = [int(i) for i in input().split()]
maze = []
start_node = None
command_node = None
for i in range(rows):
row = input()
if START in row and not start_node:
start_node = Node(i, row.index(START), maze=maze)
TARGET_NODE[START] = start_node
if COMMAND in row and not command_node:
command_node = Node(i, row.index(COMMAND), maze=maze)
TARGET_NODE[COMMAND] = command_node
maze.append(row)
current_node = Node(kr, kc, 0, 0, None, maze=maze)
if steps_remaining > 0:
steps_remaining -= 1
perr('steps_remaining', steps_remaining)
else:
if current_node.type == COMMAND:
target = START
perr(maze)
if target == COMMAND:
path_target = remaining_exploration(maze, current_node, rows, columns)
if not path_target:
path_target = TARGET_NODE[COMMAND]
else:
path_target = TARGET_NODE[START]
path = []
perr('TARGET', target)
perr('Algorithm target', path_target)
perr('current', current_node)
found = a_star_search(current_node, path_target, maze, rows, columns)
walker = found
perr('walker', walker)
while walker is not None:
path.append(walker)
walker = walker.parent
path.reverse()
perr('path', path)
prev = None
for node in path:
if prev:
print(prev.direction_to(node))
steps_remaining = len(path) - 2
prev = node
def bfs_search(current_node, path_target, maze, rows, columns):
perr('STARTING BFS')
queue = deque([current_node])
visited = []
found = None
while len(queue) > 0 and not found:
item = queue.popleft()
visited.append(item)
successors = generate_successors(maze, item, rows, columns)
perr('visited', visited)
for s in successors:
perr('s', s)
if s == path_target:
perr('FOUND TARGET')
found = s
break
elif s.type == '#':
visited.append(s)
elif path_target.type != COMMAND and s.type == COMMAND:
visited.append(s)
else:
skip = False
for o in queue:
if s == o and o < s:
skip = True
break
for c in visited:
if s == c and c < s:
skip = True
break
if not skip:
queue.append(s)
return found
def a_star_search(current_node, path_target, maze, rows, columns):
perr('STARTING A*')
open_list = deque([current_node])
closed_list = []
found = None
# A* loop
while len(open_list) > 0 and not found:
q = min(open_list)
open_list.remove(q)
successors = generate_successors(maze, q, rows, columns)
perr('q', q)
for s in successors:
if s == path_target:
perr('FOUND TARGET')
found = s
break
if s.type == '#':
h = math.inf
elif path_target.type != COMMAND and s.type == COMMAND:
h = math.inf
else:
# TODO: need a better heuristic for last 2 levels (zigzags are baaad)
h = s.distance_2_to(path_target)
s.h = h
skip = False
for o in open_list:
if s == o and o < s:
skip = True
break
for c in closed_list:
if s == c and c < s:
skip = True
break
if not skip:
open_list.append(s)
closed_list.append(q)
perr('FOUND A* PATH')
return found
def generate_successors(maze, node, max_rows, max_columns, with_walls=False, with_fog=False):
successors = []
successor_g = node.g + 1
if (node.c - 1) >= 0:
succ = Node(node.r, node.c - 1, g=successor_g, parent=node, maze=maze)
if not (succ.type == WALL and not with_walls or succ.type == FOG and not with_fog):
successors.append(succ)
if (node.r + 1) < max_rows:
succ = Node(node.r + 1, node.c, g=successor_g, parent=node, maze=maze)
if not (succ.type == WALL and not with_walls or succ.type == FOG and not with_fog):
successors.append(succ)
if (node.c + 1) < max_columns:
succ = Node(node.r, node.c + 1, g=successor_g, parent=node, maze=maze)
if not (succ.type == WALL and not with_walls or succ.type == FOG and not with_fog):
successors.append(succ)
if (node.r - 1) >= 0:
succ = Node(node.r - 1, node.c, g=successor_g, parent=node, maze=maze)
if not (succ.type == WALL and not with_walls or succ.type == FOG and not with_fog):
successors.append(succ)
return successors
def remaining_exploration(maze, node, max_rows, max_columns):
remaining = deque([node])
visited = []
while len(remaining) > 0:
cur = remaining.popleft()
visited.append(cur)
neighbors = generate_successors(maze, cur, max_rows, max_columns, with_walls=False, with_fog=True)
for n in neighbors:
if n.type == FOG:
if cur.type == COMMAND:
return cur.parent
else:
return cur
if n not in remaining and n not in visited:
remaining.append(n)
return None
def perr(*values, **kwargs):
print(*values, file=sys.stderr, flush=True, **kwargs)
if __name__ == '__main__':
main()