2022-11-05 05:36:57 +01:00
#!/usr/bin/env python3
# Copyright 2022, Jan Danielzick (aka. BodgeMaster)
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# version 3 along with this program.
# If not, see https://www.gnu.org/licenses/gpl-3.0.en.html
2022-11-06 06:22:20 +01:00
import re , time
2022-11-05 05:36:57 +01:00
2022-11-06 02:23:38 +01:00
debug_mode = False
def debug_message ( text ) :
if debug_mode :
print ( " DEBUG: " , end = " " )
print ( text )
cursor_north = ' Λ '
cursor_south = ' V '
cursor_west = ' < '
cursor_east = ' > '
cursor_current = ' '
field = [
" _________________ " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ "
2022-11-05 05:36:57 +01:00
]
wall = ' # '
apple = ' ó '
goal = ' $ '
cursor_position = [ 0 , 0 ]
2022-11-06 06:22:20 +01:00
command_delay = 0.3
2022-11-30 15:16:15 +01:00
empty_field = [
2022-11-06 02:23:38 +01:00
" _________________ " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" | | " ,
" ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ "
]
def draw_field ( ) :
2022-11-06 06:22:20 +01:00
print ( " \033 [2J \033 [H " )
2022-11-06 02:23:38 +01:00
for row in range ( len ( field ) ) :
for column in range ( len ( field [ row ] ) ) :
if column == cursor_position [ 0 ] and row == cursor_position [ 1 ] :
print ( cursor_current , end = " " )
else :
print ( field [ row ] [ column ] , end = " " )
print ( " " )
2022-11-05 05:36:57 +01:00
2022-11-06 06:21:01 +01:00
# returns "" or error message
# Parsed code is the result of running the parser,
# though at this point the input is assumed to be valid
# and no additional checks are performed on what the parser produced.
def run_code ( parsed_code ) :
for i in range ( len ( parsed_code [ 0 ] ) ) :
result = parsed_code [ 0 ] [ i ] ( * parsed_code [ 1 ] [ i ] )
if not result == " " :
return result
draw_field ( )
2022-11-06 06:22:20 +01:00
time . sleep ( command_delay )
2022-11-06 06:21:01 +01:00
return " "
2022-11-05 05:36:57 +01:00
def condition_facing_north ( inverted ) :
2022-11-06 02:24:21 +01:00
if inverted :
return not cursor_current == cursor_north
return cursor_current == cursor_north
2022-11-05 05:36:57 +01:00
def condition_facing_south ( inverted ) :
2022-11-06 02:24:21 +01:00
if inverted :
return not cursor_current == cursor_south
return cursor_current == cursor_south
2022-11-05 05:36:57 +01:00
def condition_facing_east ( inverted ) :
2022-11-06 02:24:21 +01:00
if inverted :
return not cursor_current == cursor_east
return cursor_current == cursor_east
2022-11-05 05:36:57 +01:00
def condition_facing_west ( inverted ) :
2022-11-06 02:24:21 +01:00
if inverted :
return not cursor_current == cursor_west
return cursor_current == cursor_west
2022-11-05 05:36:57 +01:00
2022-11-06 02:24:21 +01:00
def condition_in_front_of_wall ( inverted ) :
if condition_facing_north ( False ) :
result = field [ cursor_position [ 1 ] - 1 ] [ cursor_position [ 0 ] ] == wall or field [ cursor_position [ 1 ] - 1 ] [ cursor_position [ 0 ] ] == ' _ '
elif condition_facing_south ( False ) :
result = field [ cursor_position [ 1 ] + 1 ] [ cursor_position [ 0 ] ] == wall or field [ cursor_position [ 1 ] + 1 ] [ cursor_position [ 0 ] ] == ' ¯ '
elif condition_facing_east ( False ) :
result = field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] + 1 ] == wall or field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] + 1 ] == ' | '
elif condition_facing_west ( False ) :
result = field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] - 1 ] == wall or field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] - 1 ] == ' | '
else :
result = False
if inverted :
return not result
return result
def condition_goal_reached ( inverted ) :
if inverted :
2022-11-06 03:30:03 +01:00
return not field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] ] == goal
return field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] ] == goal
2022-11-06 02:24:21 +01:00
def condition_on_apple ( inverted ) :
if inverted :
2022-11-06 03:30:03 +01:00
return not field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] ] == apple
return field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] ] == apple
2022-11-05 05:36:57 +01:00
# return value: "" or error message
def command_step ( ) :
2022-11-06 03:30:58 +01:00
if condition_in_front_of_wall ( False ) :
2022-11-06 06:22:20 +01:00
return " You stepped into a wall. "
2022-11-06 03:30:58 +01:00
if condition_facing_north ( False ) :
cursor_position [ 1 ] = cursor_position [ 1 ] - 1
elif condition_facing_south ( False ) :
cursor_position [ 1 ] = cursor_position [ 1 ] + 1
elif condition_facing_east ( False ) :
cursor_position [ 0 ] = cursor_position [ 0 ] + 1
elif condition_facing_west ( False ) :
cursor_position [ 0 ] = cursor_position [ 0 ] - 1
else :
return " Cannot step because direction is unknown. "
2022-11-05 05:36:57 +01:00
return " "
def command_left ( ) :
2022-11-06 03:30:58 +01:00
global cursor_current
if condition_facing_north ( False ) :
cursor_current = cursor_west
elif condition_facing_south ( False ) :
cursor_current = cursor_east
elif condition_facing_east ( False ) :
cursor_current = cursor_north
elif condition_facing_west ( False ) :
cursor_current = cursor_south
else :
return " Cannot turn left because direction is unknown. "
2022-11-05 05:36:57 +01:00
return " "
def command_right ( ) :
2022-11-06 03:30:58 +01:00
global cursor_current
if condition_facing_north ( False ) :
cursor_current = cursor_east
elif condition_facing_south ( False ) :
cursor_current = cursor_west
elif condition_facing_east ( False ) :
cursor_current = cursor_south
elif condition_facing_west ( False ) :
cursor_current = cursor_north
else :
return " Cannot turn right because direction is unknown. "
2022-11-05 05:36:57 +01:00
return " "
2022-11-06 03:30:58 +01:00
def command_take ( ) :
if condition_on_apple ( True ) :
# Note: condition is inverted
return " Cannot take apple, there is no apple here. "
field [ cursor_position [ 1 ] ] = field [ cursor_position [ 1 ] ] [ : cursor_position [ 0 ] ] + " " + field [ cursor_position [ 1 ] ] [ cursor_position [ 0 ] + 1 : ]
2022-11-05 05:36:57 +01:00
return " "
2022-11-06 03:30:58 +01:00
def command_repeat ( number , parsed_code ) :
2022-11-06 06:18:07 +01:00
for i in range ( number ) :
result = run_code ( parsed_code )
if not result == " " :
return result
2022-11-05 05:36:57 +01:00
return " "
2022-11-06 03:30:58 +01:00
def command_while ( parsed_condition , parsed_code ) :
2022-11-06 06:18:07 +01:00
while parsed_condition [ 0 ] ( parsed_condition [ 1 ] ) :
result = run_code ( parsed_code )
if not result == " " :
return result
2022-11-05 05:36:57 +01:00
return " "
def command_if ( parsed_condition , parsed_then_code , parsed_else_code ) :
2022-11-06 06:18:07 +01:00
if parsed_condition [ 0 ] ( parsed_condition [ 1 ] ) :
result = run_code ( parsed_then_code )
if not result == " " :
return result
elif not parsed_else_code == None :
result = run_code ( parsed_else_code )
if not result == " " :
return result
2022-11-05 05:36:57 +01:00
return " "
# returns -1 for generic error
# returns -2 if end can’ t be found
def get_length_of_condition ( code ) :
if len ( code ) == 0 :
return - 1
if code [ 0 ] == ' < ' :
length = 0
while length < len ( code ) and code [ length ] != ' > ' :
length = length + 1
if length == len ( code ) and code [ - 1 ] != ' > ' :
return - 2
return length + 1
else :
return - 1
# returns -1 for generic error
# returns -2 if end can’ t be found
def get_length_of_contained_code ( code ) :
if len ( code ) == 0 :
return - 1
if code [ 0 ] == ' ( ' :
length = 1
nesting_level = 1
while length < len ( code ) and nesting_level > 0 :
if code [ length ] == ' ( ' :
nesting_level = nesting_level + 1
elif code [ length ] == ' ) ' :
nesting_level = nesting_level - 1
length = length + 1
if length == len ( code ) and nesting_level > 0 :
return - 2
return length
else :
return - 1
# clean up whitespace
def format_code ( code ) :
regex_pattern = re . compile ( r ' \ s+ ' )
code = " " . join ( re . sub ( regex_pattern , ' ' , code ) . lower ( ) . split ( ) )
code = code . replace ( " ( " , " ( " )
code = code . replace ( " ( " , " ( " )
code = code . replace ( " ) " , " ) " )
code = code . replace ( " ) " , " ) " )
code = code . replace ( " < " , " < " )
code = code . replace ( " < " , " < " )
code = code . replace ( " > " , " > " )
code = code . replace ( " < " , " < " )
code = code . replace ( " ! " , " ! " )
# add spaces back after closing parenthesis
code = code . replace ( " ) " , " ) " )
code = code . replace ( " ) ) " , " )) " )
code = code . replace ( " ) ) " , " )) " )
if len ( code ) > 0 and code [ - 1 ] == ' ' :
return code [ : - 1 ]
return code
# returns [function, inverted?, error message]
def parse_condition ( condition_code , allowed_conditions ) :
inverted = False
condition = None
if len ( condition_code ) == 0 :
debug_message ( " No condition specified " )
return [ condition , inverted , " Syntax error: No condition specified " ]
if condition_code [ 0 ] == ' ! ' :
debug_message ( " Condition is inverted " )
inverted = True
condition_code = condition_code [ 1 : ]
if len ( condition_code ) == 0 :
debug_message ( " No condition specified " )
return [ condition , inverted , " Syntax error: No condition specified " ]
if condition_code == " in front of wall " :
condition = condition_in_front_of_wall
elif condition_code == " goal reached " :
condition = condition_goal_reached
elif condition_code == " on apple " :
condition = condition_on_apple
elif condition_code == " facing north " :
condition = condition_facing_north
elif condition_code == " facing south " :
condition = condition_facing_south
elif condition_code == " facing east " :
condition = condition_facing_east
elif condition_code == " facing west " :
condition = condition_facing_west
else :
debug_message ( " Unknown condition: " + condition_code )
return [ condition , inverted , " Unknown condition: " + condition_code ]
2022-11-26 15:19:02 +01:00
if not condition_code in allowed_conditions :
return [ condition , inverted , " Condition is not allowed here: " + condition_code ]
2022-11-05 05:36:57 +01:00
return [ condition , inverted , " " ]
2022-11-26 15:19:02 +01:00
# returns [[functions], [arguments], -1, "", "formatted code"] or [[], [], error_position, "error message", "formatted code"]
2022-11-05 05:36:57 +01:00
def parse_code ( code , allowed_commands , allowed_conditions , unformatted_code = True ) :
if unformatted_code :
code = format_code ( code )
debug_message ( " Formatted code: " + code )
parse_position = 0
2022-11-06 03:51:31 +01:00
parsed_code = [ [ ] , [ ] , - 1 , " " , code ]
2022-11-05 05:36:57 +01:00
while parse_position < len ( code ) :
# find next command
next_space = code . find ( ' ' , parse_position )
next_condition = code . find ( ' < ' , parse_position )
if next_space == - 1 :
next_space = len ( code )
if next_condition == - 1 :
next_condition = len ( code )
# Are we working with a simple command?
if next_condition > next_space or next_condition == next_space :
next_command = code [ parse_position : next_space ]
debug_message ( " Next command is: " + next_command )
parsed_code [ 1 ] . append ( ( ) )
if next_command == " step " :
parsed_code [ 0 ] . append ( command_step )
2022-11-26 15:19:02 +01:00
if not next_command in allowed_commands :
return [ [ ] , [ ] , parse_position , " Command is not allowed here: " + next_command , code ]
2022-11-05 05:36:57 +01:00
elif next_command == " left " :
parsed_code [ 0 ] . append ( command_left )
2022-11-26 15:19:02 +01:00
if not next_command in allowed_commands :
return [ [ ] , [ ] , parse_position , " Command is not allowed here: " + next_command , code ]
2022-11-05 05:36:57 +01:00
elif next_command == " right " :
parsed_code [ 0 ] . append ( command_right )
2022-11-26 15:19:02 +01:00
if not next_command in allowed_commands :
return [ [ ] , [ ] , parse_position , " Command is not allowed here: " + next_command , code ]
2022-11-05 05:36:57 +01:00
elif next_command == " take " :
parsed_code [ 0 ] . append ( command_take )
2022-11-26 15:19:02 +01:00
if not next_command in allowed_commands :
return [ [ ] , [ ] , parse_position , " Command is not allowed here: " + next_command , code ]
2022-11-05 05:36:57 +01:00
elif next_command == " repeat " :
2022-11-06 06:22:20 +01:00
return [ [ ] , [ ] , next_space , " Syntax error: Number of repetitions missing " , code ]
2022-11-05 05:36:57 +01:00
elif next_command == " while " :
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , next_space , " Syntax error: Condition missing " , code ]
2022-11-05 05:36:57 +01:00
elif next_command == " if " :
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , next_space , " Syntax error: Condition missing " , code ]
elif next_command [ 0 : 4 ] == " else " :
return [ [ ] , [ ] , parse_position , " Syntax error: Else without if statement " , code ]
2022-11-05 05:36:57 +01:00
else :
#TODO: better error message (especially when special chars are detected inside next_command or a known command)
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Unknown command: " + next_command , code ]
2022-11-05 05:36:57 +01:00
parse_position = next_space + 1
else :
debug_message ( " Found control structure... " )
control_structure = code [ parse_position : next_condition ]
if control_structure == " repeat " :
debug_message ( " Type: repeat loop " )
2022-11-26 15:19:02 +01:00
# This is checked here because otherwise it would throw a bogus error message for unknown control structures.
if not control_structure in allowed_commands :
return [ [ ] , [ ] , parse_position , " Command is not allowed here: " + control_structure , code ]
2022-11-05 05:36:57 +01:00
parse_position = next_condition
repetitions_length = get_length_of_condition ( code [ parse_position : ] )
if repetitions_length == - 2 :
debug_message ( " Mismatched <> " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find matching ' > ' for this ' < ' " , code ]
2022-11-05 05:36:57 +01:00
repetitions = code [ parse_position : parse_position + repetitions_length ]
debug_message ( " Number of repetitions: " + repetitions )
repetitions_int = 0
try :
repetitions_int = int ( repetitions [ 1 : - 1 ] )
except ValueError :
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Not a valid number: " + repetitions [ 1 : - 1 ] , code ]
2022-11-05 05:36:57 +01:00
parse_position = parse_position + len ( repetitions )
contained_code_length = get_length_of_contained_code ( code [ parse_position : ] )
if contained_code_length == - 1 :
debug_message ( " Missing ( " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find ' ( ' for contained code " , code ]
2022-11-05 05:36:57 +01:00
if contained_code_length == - 2 :
debug_message ( " Mismatched () " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find matching ' ) ' for this ' ( ' " , code ]
2022-11-05 05:36:57 +01:00
contained_code = code [ parse_position : parse_position + contained_code_length ]
debug_message ( " Contained code: " + contained_code )
parsed_contained_code = parse_code ( contained_code [ 1 : - 1 ] , allowed_commands , allowed_conditions , unformatted_code = False )
if not parsed_contained_code [ 2 ] == - 1 :
debug_message ( " Error while parsing contained code " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position + 1 + parsed_contained_code [ 2 ] , parsed_contained_code [ 3 ] , code ]
2022-11-05 05:36:57 +01:00
parse_position = parse_position + contained_code_length + 1
parsed_code [ 1 ] . append ( ( repetitions_int , parsed_contained_code ) )
parsed_code [ 0 ] . append ( command_repeat )
continue
elif control_structure == " if " :
debug_message ( " Type: if statement " )
2022-11-26 15:19:02 +01:00
# This is checked here because otherwise it would throw a bogus error message for unknown control structures.
if not control_structure in allowed_commands :
return [ [ ] , [ ] , parse_position , " Command is not allowed here: " + control_structure , code ]
2022-11-05 05:36:57 +01:00
parse_position = next_condition
condition_length = get_length_of_condition ( code [ parse_position : ] )
if condition_length == - 2 :
debug_message ( " Mismatched <> " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find matching ' > ' for this ' < ' " , code ]
2022-11-05 05:36:57 +01:00
condition = code [ parse_position : parse_position + condition_length ]
debug_message ( " Condition is: " + condition )
parsed_condition = parse_condition ( condition [ 1 : - 1 ] , allowed_conditions )
if not parsed_condition [ 2 ] == " " :
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , parsed_condition [ 2 ] , code ]
2022-11-05 05:36:57 +01:00
parse_position = parse_position + len ( condition )
then_code_length = get_length_of_contained_code ( code [ parse_position : ] )
if then_code_length == - 1 :
debug_message ( " Missing ( " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find ' ( ' for then-code " , code ]
2022-11-05 05:36:57 +01:00
if then_code_length == - 2 :
debug_message ( " Mismatched () " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find matching ' ) ' for this ' ( ' " , code ]
2022-11-05 05:36:57 +01:00
then_code = code [ parse_position : parse_position + then_code_length ]
debug_message ( " Then-code: " + then_code )
parsed_then_code = parse_code ( then_code [ 1 : - 1 ] , allowed_commands , allowed_conditions , unformatted_code = False )
if not parsed_then_code [ 2 ] == - 1 :
debug_message ( " Error while parsing then-code " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position + 1 + parsed_then_code [ 2 ] , parsed_then_code [ 3 ] , code ]
2022-11-05 05:36:57 +01:00
parse_position = parse_position + then_code_length + 1
parsed_else_code = None
if parse_position < len ( code ) + 4 and code [ parse_position : parse_position + 4 ] == " else " :
parse_position = parse_position + 4
else_code_length = get_length_of_contained_code ( code [ parse_position : ] )
if else_code_length == - 1 :
debug_message ( " Missing ( " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find ' ( ' for else-code " , code ]
2022-11-05 05:36:57 +01:00
if else_code_length == - 2 :
debug_message ( " Mismatched () " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find matching ' ) ' for this ' ( ' " , code ]
2022-11-05 05:36:57 +01:00
else_code = code [ parse_position : parse_position + else_code_length ]
debug_message ( " Else-code: " + else_code )
parsed_else_code = parse_code ( else_code [ 1 : - 1 ] , allowed_commands , allowed_conditions , unformatted_code = False )
parse_position = parse_position + else_code_length + 1
parsed_code [ 1 ] . append ( ( parsed_condition , parsed_then_code , parsed_else_code ) )
parsed_code [ 0 ] . append ( command_if )
continue
elif control_structure == " while " :
debug_message ( " Type: while loop " )
2022-11-26 15:19:02 +01:00
# This is checked here because otherwise it would throw a bogus error message for unknown control structures.
if not control_structure in allowed_commands :
return [ [ ] , [ ] , parse_position , " Command is not allowed here: " + control_structure , code ]
2022-11-05 05:36:57 +01:00
parse_position = next_condition
condition_length = get_length_of_condition ( code [ parse_position : ] )
if condition_length == - 2 :
debug_message ( " Mismatched <> " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find matching ' > ' for this ' < ' " , code ]
2022-11-05 05:36:57 +01:00
condition = code [ parse_position : parse_position + condition_length ]
debug_message ( " Condition is: " + condition )
parsed_condition = parse_condition ( condition [ 1 : - 1 ] , allowed_conditions )
if not parsed_condition [ 2 ] == " " :
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , parsed_condition [ 2 ] , code ]
2022-11-05 05:36:57 +01:00
parse_position = parse_position + len ( condition )
contained_code_length = get_length_of_contained_code ( code [ parse_position : ] )
if contained_code_length == - 1 :
debug_message ( " Missing ( " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find ' ( ' for contained code " , code ]
2022-11-05 05:36:57 +01:00
if contained_code_length == - 2 :
debug_message ( " Mismatched () " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Cannot not find matching ' ) ' for this ' ( ' " , code ]
2022-11-05 05:36:57 +01:00
contained_code = code [ parse_position : parse_position + contained_code_length ]
debug_message ( " Contained code: " + contained_code )
parsed_contained_code = parse_code ( contained_code [ 1 : - 1 ] , allowed_commands , allowed_conditions , unformatted_code = False )
if not parsed_contained_code [ 2 ] == - 1 :
debug_message ( " Error while parsing contained code " )
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position + 1 + parsed_contained_code [ 2 ] , parsed_contained_code [ 3 ] , code ]
2022-11-05 05:36:57 +01:00
parse_position = parse_position + contained_code_length + 1
parsed_code [ 1 ] . append ( ( parsed_condition , parsed_contained_code ) )
parsed_code [ 0 ] . append ( command_while )
else :
debug_message ( " Type: unknown control structure: " + control_structure )
#TODO: better error message (especially when special chars are detected inside control_structure or a known command)
2022-11-06 03:51:31 +01:00
return [ [ ] , [ ] , parse_position , " Syntax error: Unknown control structure: " + control_structure , code ]
2022-11-05 05:36:57 +01:00
return parsed_code
2022-11-06 06:21:01 +01:00
# A wrapper for run_code that deals with potential errors produced by the parser.
2022-11-05 05:36:57 +01:00
# returns success or error message
2022-11-06 06:21:01 +01:00
def evaluate_parser_result ( parsed_code ) :
if not parsed_code [ 2 ] == - 1 :
# parser error
return parsed_code [ 3 ] + " \n " + ( " " * parsed_code [ 2 ] ) + " ↓ \n " + parsed_code [ 4 ]
outcome = run_code ( parsed_code )
if not outcome == " " :
# runtime error
# TODO: pass back information about where the error occurred
return outcome
2022-11-26 15:05:52 +01:00
if condition_goal_reached ( False ) :
return " Success! "
else :
return " Goal not reached! "
2022-11-05 05:36:57 +01:00
2022-11-06 06:22:20 +01:00
2022-11-30 15:16:15 +01:00
def setup_and_run_task ( start_field , start_position , start_heading , text , allowed_commands , allowed_conditions ) :
2022-11-06 06:22:20 +01:00
global cursor_position
2022-11-30 15:16:15 +01:00
cursor_position = start_position
2022-11-06 06:22:20 +01:00
global cursor_current
2022-11-30 15:16:15 +01:00
cursor_current = start_heading
global field
field = start_field
2022-11-06 06:22:20 +01:00
draw_field ( )
2022-11-30 15:16:15 +01:00
print ( " \n " + text )
2022-12-23 12:17:21 +01:00
print ( " \n Allowed commands: " + " , " . join ( allowed_commands ) )
2023-01-06 15:10:45 +01:00
if len ( allowed_conditions ) > 0 :
print ( " \n Available conditions: " + " , " . join ( allowed_conditions ) )
2022-12-23 12:56:57 +01:00
result = evaluate_parser_result ( parse_code ( input ( " \n Code: " ) , allowed_commands , allowed_conditions ) )
print ( result )
2023-01-06 15:10:12 +01:00
time . sleep ( 3 )
2022-12-23 12:56:57 +01:00
if result == " Success! " :
return True
return False
2022-11-30 15:16:15 +01:00
2022-11-05 05:36:57 +01:00
if __name__ == " __main__ " :
2022-12-23 12:18:11 +01:00
import sys , os , json
level = 0
single_file_mode = False
level_file_name = " "
def wrong_usage ( ) :
print ( " Usage: \n " + sys . argv [ 0 ] + " [l<number> | level_file_name.json] \n \n - Run without arguments to start at level 0. \n - Run with a single argument made of ' l ' and a number to start at that level. \n - Run with a file name to start that specific level. " )
sys . exit ( 1 )
if len ( sys . argv ) > 1 :
if len ( sys . argv ) > 2 :
wrong_usage ( )
if sys . argv [ 1 ] [ 0 ] == " l " :
try :
level = int ( sys . argv [ 1 ] [ 1 : ] )
except :
if os . path . isfile ( sys . argv [ 1 ] ) :
level_file_name = sys . argv [ 1 ]
single_file_mode = True
else :
wrong_usage ( )
elif os . path . isfile ( sys . argv [ 1 ] ) :
level_file_name = sys . argv [ 1 ]
single_file_mode = True
else :
wrong_usage ( )
if not single_file_mode :
level_file_name = os . path . join ( " levels " , str ( level ) + " .json " )
while os . path . isfile ( level_file_name ) :
level_file = open ( level_file_name , " r " )
level_raw = level_file . read ( )
level_file . close ( )
level_data = json . loads ( level_raw )
cursor_start = " "
if level_data [ " start heading " ] == " north " :
cursor_start = cursor_north
elif level_data [ " start heading " ] == " south " :
cursor_start = cursor_south
elif level_data [ " start heading " ] == " east " :
cursor_start = cursor_east
elif level_data [ " start heading " ] == " west " :
cursor_start = cursor_west
else :
print ( " Unknown start heading! " )
sys . exit ( 1 )
2022-12-23 12:56:57 +01:00
while not setup_and_run_task ( level_data [ " field " ] . copy ( ) , level_data [ " start position " ] . copy ( ) , cursor_start , level_data [ " text " ] , level_data [ " allowed commands " ] . copy ( ) , level_data [ " allowed conditions " ] . copy ( ) ) :
2023-01-06 15:10:12 +01:00
pass
2022-12-23 12:18:11 +01:00
if single_file_mode :
break
level + = 1
level_file_name = os . path . join ( " levels " , str ( level ) + " .json " )