#!/usr/bin/env python3 """Convert C++ game code (int main / void functions) to Python style.""" import re from typing import Optional def _remove_comments(code: str) -> str: return re.sub(r'//[^\n]*', '', code) def _find_matching_brace(code: str, open_pos: int) -> int: depth = 0 for i in range(open_pos, len(code)): if code[i] == '{': depth += 1 elif code[i] == '}': depth -= 1 if depth == 0: return i return -1 def _find_matching_paren(code: str, open_pos: int) -> int: depth = 0 for i in range(open_pos, len(code)): if code[i] == '(': depth += 1 elif code[i] == ')': depth -= 1 if depth == 0: return i return -1 def _parse_for_header(header: str) -> Optional[str]: header = re.sub(r'\s+', ' ', header.strip()) m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)\+\+\s*\)', header, ) if m: var, start, _, op, end, _ = m.groups() return _range_for(var, start.strip(), op, end.strip(), 1) m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)\s*=\s*(\w+)\s*([+\-])\s*(\d+)\s*\)', header, ) if m: var, start, _, op, end, _, _, sign, step = m.groups() step_n = int(step) if sign == '+' else -int(step) return _range_for(var, start.strip(), op, end.strip(), step_n) m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)\+=\s*(\d+)\s*\)', header, ) if m: var, start, _, op, end, _, step = m.groups() return _range_for(var, start.strip(), op, end.strip(), int(step)) m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)-=\s*(\d+)\s*\)', header, ) if m: var, start, _, op, end, _, step = m.groups() return _range_for(var, start.strip(), op, end.strip(), -int(step)) m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)\s*=\s*(\w+)\s*-\s*(\d+)\s*\)', header, ) if m: var, start, _, op, end, _, _, step = m.groups() return _range_for(var, start.strip(), op, end.strip(), -int(step)) m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)--\s*\)', header, ) if m: var, start, _, op, end, _ = m.groups() return _range_for(var, start.strip(), op, end.strip(), -1) m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)\s*\*=\s*(\d+)\s*\)', header, ) if m: var, start, _, op, end, _, mult = m.groups() return f'WHILE|{var}|{start.strip()}|{op}|{end.strip()}|{var} *= {mult}' m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)\s*=\s*\1\s*\*\s*(\d+)\s*\)', header, ) if m: var, start, _, op, end, _, mult = m.groups() return f'WHILE|{var}|{start.strip()}|{op}|{end.strip()}|{var} *= {mult}' m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)\s*/=\s*(\d+)\s*\)', header, ) if m: var, start, _, op, end, _, div = m.groups() return f'WHILE|{var}|{start.strip()}|{op}|{end.strip()}|{var} //= {div}' m = re.match( r'for\s*\(\s*int\s+(\w+)\s*=\s*([^;]+);\s*(\w+)\s*([<>=!]+)\s*([^;]+);\s*(\w+)\s*=\s*\1\s*/\s*(\d+)\s*\)', header, ) if m: var, start, _, op, end, _, div = m.groups() return f'WHILE|{var}|{start.strip()}|{op}|{end.strip()}|{var} //= {div}' return None def _add_one(expr: str) -> str: expr = expr.strip() if re.fullmatch(r'-?\d+', expr): return str(int(expr) + 1) return f'{expr} + 1' def _sub_one(expr: str) -> str: expr = expr.strip() if re.fullmatch(r'-?\d+', expr): return str(int(expr) - 1) return f'{expr} - 1' def _range_for(var: str, start: str, op: str, end: str, step: int) -> str: if step == 1: if op == '<': if start == '0': return f'for {var} in range({end}):' return f'for {var} in range({start}, {end}):' if op == '<=': return f'for {var} in range({start}, {_add_one(end)}):' if step == -1: if op == '>': return f'for {var} in range({start}, {end}, -1):' if op == '>=': return f'for {var} in range({start}, {_sub_one(end)}, -1):' if step > 1 and op == '<': return f'for {var} in range({start}, {end}, {step}):' if step < -1: if op == '>=': return f'for {var} in range({start}, {_sub_one(end)}, {step}):' if op == '>': return f'for {var} in range({start}, {end}, {step}):' return f'# UNCONVERTED FOR: {var} {start} {op} {end} step={step}' def _split_args(s: str) -> list[str]: parts, cur, depth = [], [], 0 for c in s: if c in '([': depth += 1 elif c in ')]': depth -= 1 elif c == ',' and depth == 0: parts.append(''.join(cur).strip()) cur = [] continue cur.append(c) if cur: parts.append(''.join(cur).strip()) return parts def _format_rhs(expr: str) -> str: expr = expr.strip().rstrip(';') m = re.match(r'(\w+)\((.*)\)$', expr) if m: fn, args = m.group(1), m.group(2) if not args.strip(): return f'{fn}()' parts = _split_args(args) return f'{fn}({", ".join(_format_rhs(p) for p in parts)})' expr = re.sub(r'\s+', '', expr) tokens = re.split(r'(==|!=|<=|>=|[+\-*/=<>])', expr) parts = [] for t in tokens: if not t: continue if t in '+-*/=<>!': parts.append(f' {t} ') elif t in ('==', '!=', '<=', '>='): parts.append(f' {t} ') else: parts.append(t) result = ''.join(parts) result = re.sub(r'\s+', ' ', result).strip() result = re.sub(r'^-\s+(\d)', r'-\1', result) result = re.sub(r'-\s+(\w)', r'-\1', result) result = re.sub(r'\(\s*-\s+(\w)', r'(-\1', result) return result def _format_stmt(stmt: str) -> str: stmt = stmt.strip().rstrip(';') m = re.match(r'int\s+(\w+)\s*=\s*(.+)', stmt) if m: return f'{m.group(1)} = {_format_rhs(m.group(2))}' m = re.match(r'(\w+)\s*=\s*(.+)', stmt) if m and not stmt.startswith(('if ', 'for ', 'void ', 'return')): return f'{m.group(1)} = {_format_rhs(m.group(2))}' return _format_rhs(stmt) def _skip_ws(code: str) -> str: return code.lstrip(' \t\n\r') def _convert_block(code: str, indent: int = 0) -> list[str]: lines: list[str] = [] code = code.strip() pad = ' ' * indent while code: code = _skip_ws(code) if not code: break # void function m = re.match(r'void\s+(\w+)\s*\(([^)]*)\)\s*\{', code) if m: fn, params = m.group(1), m.group(2).strip() if params: names = [p.strip().split()[-1] for p in params.split(',') if p.strip()] lines.append(f'def {fn}({", ".join(names)}):') else: lines.append(f'def {fn}():') ob = code.index('{', m.start()) cb = _find_matching_brace(code, ob) lines.extend(_convert_block(code[ob + 1:cb], indent + 1)) lines.append('') code = code[cb + 1:] continue # int main() m = re.match(r'int\s+main\s*\(\s*\)\s*\{', code) if m: ob = code.index('{', m.start()) cb = _find_matching_brace(code, ob) lines.extend(_convert_block(code[ob + 1:cb], indent)) code = code[cb + 1:] continue # for loop m = re.match(r'for\s*\(', code) if m: cp = _find_matching_paren(code, m.end() - 1) header = code[m.start():cp + 1] rest = _skip_ws(code[cp + 1:]) if rest.startswith('{'): cb = _find_matching_brace(rest, 0) body = rest[1:cb] py_for = _parse_for_header(header) if py_for and py_for.startswith('WHILE|'): _, var, start, op, end, inc = py_for.split('|') lines.append(pad + f'{var} = {start}') lines.append(pad + f'while {var} {op} {end}:') lines.extend(_convert_block(body, indent + 1)) lines.append(pad + f' {inc}') elif py_for: lines.append(pad + py_for) lines.extend(_convert_block(body, indent + 1)) else: lines.append(pad + (py_for or f'# {header}')) lines.extend(_convert_block(body, indent + 1)) code = rest[cb + 1:] continue # if / else if / else m = re.match(r'if\s*\(', code) if m: cp = _find_matching_paren(code, m.end() - 1) cond = code[m.end():cp].strip() rest = _skip_ws(code[cp + 1:]) if rest.startswith('{'): cb = _find_matching_brace(rest, 0) if_body = rest[1:cb] lines.append(pad + f'if {_format_rhs(cond)}:') lines.extend(_convert_block(if_body, indent + 1)) code = _skip_ws(rest[cb + 1:]) if code.startswith('else if'): sub_lines, code = _convert_if_chain(code, indent, is_elif=True) lines.extend(sub_lines) elif code.startswith('else'): if code.startswith('else {'): ecb = _find_matching_brace(code, code.index('{')) lines.append(pad + 'else:') lines.extend(_convert_block(code[code.index('{') + 1:ecb], indent + 1)) code = code[ecb + 1:] elif code.startswith('else if'): pass # handled above continue # return m = re.match(r'return\s+0\s*;', code) if m: code = code[m.end():] continue # statement until ; semi = -1 depth = 0 for j, c in enumerate(code): if c in '{(': depth += 1 elif c in '})': depth -= 1 elif c == ';' and depth == 0: semi = j break if semi >= 0: stmt = code[:semi].strip() if stmt and not re.match(r'return\s+0', stmt): lines.append(pad + _format_stmt(stmt)) code = code[semi + 1:] continue # trailing statement without semicolon stmt = code.strip() if stmt and not re.match(r'return\s+0', stmt): lines.append(pad + _format_stmt(stmt)) break return lines def _convert_if_chain(code: str, indent: int, is_elif: bool = False) -> tuple[list[str], str]: lines: list[str] = [] pad = ' ' * indent prefix = 'elif' if is_elif else 'if' while True: code = _skip_ws(code) if code.startswith('else if'): code = code[7:].lstrip() prefix = 'elif' elif code.startswith('if'): code = code[2:].lstrip() elif code.startswith('else'): if code.startswith('else {'): cb = _find_matching_brace(code, code.index('{')) lines.append(pad + 'else:') lines.extend(_convert_block(code[code.index('{') + 1:cb], indent + 1)) return lines, code[cb + 1:] break else: break if not code.startswith('('): break cp = _find_matching_paren(code, 0) cond = code[1:cp].strip() rest = _skip_ws(code[cp + 1:]) if not rest.startswith('{'): break cb = _find_matching_brace(rest, 0) lines.append(pad + f'{prefix} {_format_rhs(cond)}:') lines.extend(_convert_block(rest[1:cb], indent + 1)) code = _skip_ws(rest[cb + 1:]) if code.startswith('else if'): prefix = 'elif' code = code[4:].lstrip() # 'else if' -> ' if' wait code = 'if' + code[2:] if code.startswith(' if') else code continue if code.startswith('else {'): cb2 = _find_matching_brace(code, code.index('{')) lines.append(pad + 'else:') lines.extend(_convert_block(code[code.index('{') + 1:cb2], indent + 1)) return lines, code[cb2 + 1:] return lines, code return lines, code def cpp_to_python(cpp_code: str) -> str: if not cpp_code or not str(cpp_code).strip(): return '' code = _remove_comments(str(cpp_code)) lines = _convert_block(code, 0) return '\n'.join(lines).strip() if __name__ == '__main__': import sys print(cpp_to_python(sys.stdin.read()))