Creación de un lenguaje propio
Así es, para DSLD me han pedido que cree un lenguaje de programación propio. Tras algo de brainstorming se me ha ocurrido hacer un animador por máquinas de estados. En el definirías texturas, animaciones, estados y transiciones, además de la lógica para cambiar entre ellos.
| AnimationStateMachine "Spaceship" {
var fuel = 100 clamp 0 100
var heat = 0 clamp 0 100
var speed = 0 clamp 0 3
var shields = 100 clamp 0 100
Inputs {
"w" -> thrust
"s" -> brake
"d" -> dock
"o" -> overdrive
}
Texture "ship_docked" {
" [=======] "
" | DOCK | "
" /| |\ "
"/_========= _\"
}
Texture "ship_idle" {
" ^ "
" / \ "
" | O | "
" /|___|\ "
" /_/ \_\ "
}
Texture "ship_thrust_1" {
" ^ "
" / \ "
" | O | "
" /|___|\ "
" /_/ W \_\ "
}
Texture "ship_thrust_2" {
" ^ "
" / \ "
" | O | "
" /|___|\ "
" /_/ w \_\ "
}
Texture "ship_overheat" {
" ^ "
" /!\ "
" | O | "
" /|___|\ "
" /_/!!!\_\ "
}
Texture "ship_destroyed" {
" "
" \ | / "
" - X X - "
" / | \ "
" "
}
Animation "thrust_anim" {
cyclic: true
frames: ["ship_thrust_1", "ship_thrust_1", "ship_thrust_2", "ship_thrust_2"]
}
initial_state: Docked
State Docked {
on_update {
fuel = fuel + 5
if (heat > 0) { heat = heat - 5 }
if (shields < 100) { shields = shields + 2 }
}
on_signal "thrust" {
if (fuel > 10) {
speed = 1
transition_to Cruising
}
}
render {
use "ship_docked"
}
}
State Cruising {
on_update {
if (speed > 0) {
fuel = fuel - speed
heat = heat + speed
if (heat > 80) {
transition_to Overheating
} else {
if (fuel == 0) {
speed = 0
transition_to Stranded
}
}
} else {
if (heat > 0) { heat = heat - 1 }
}
}
on_signal "thrust" {
if (speed < 3) { speed = speed + 1 }
}
on_signal "brake" {
if (speed > 0) { speed = speed - 1 }
}
on_signal "dock" {
if (speed == 0) { transition_to Docked }
}
on_signal "overdrive" {
if (fuel > 20) {
speed = 3
heat = heat + 30
}
}
render {
if (speed == 0) {
use "ship_idle"
} else {
use "thrust_anim"
}
}
}
State Overheating {
on_enter {
print "WARNING: CRITICAL CORE TEMPERATURE"
}
on_update {
shields = shields - 5
if (heat > 0) { heat = heat - 2 }
if (shields == 0) {
transition_to Destroyed
} else {
if (heat < 50) {
transition_to Cruising
}
}
}
on_signal "brake" {
speed = 0
}
render {
use "ship_overheat"
}
}
State Stranded {
on_update {
if (heat > 0) { heat = heat - 1 }
}
render {
use "ship_idle"
}
}
State Destroyed {
on_enter {
speed = 0
fuel = 0
}
render {
use "ship_destroyed"
}
}
}
|
El código anterior sería compilado a este programa en python:
| import time
import os
import sys
import select
import termios
import tty
def clear_screen():
os.system('cls' if os.name == 'nt' else 'clear')
class SpaceshipAnimation:
def __init__(self):
self.state = "Docked"
self.state_time = 0.0
self._fuel = 100
self.fuel_max = 100
self.fuel_min = 0
self._heat = 0
self.heat_max = 100
self.heat_min = 0
self._speed = 0
self.speed_max = 3
self.speed_min = 0
self._shields = 100
self.shields_max = 100
self.shields_min = 0
self.input_map = {
'w': 'thrust',
's': 'brake',
'd': 'dock',
'o': 'overdrive'
}
@property
def fuel(self): return self._fuel
@fuel.setter
def fuel(self, val): self._fuel = max(self.fuel_min, min(self.fuel_max, val))
@property
def heat(self): return self._heat
@heat.setter
def heat(self, val): self._heat = max(self.heat_min, min(self.heat_max, val))
@property
def speed(self): return self._speed
@speed.setter
def speed(self, val): self._speed = max(self.speed_min, min(self.speed_max, val))
@property
def shields(self): return self._shields
@shields.setter
def shields(self, val): self._shields = max(self.shields_min, min(self.shields_max, val))
def send_signal(self, signal):
if self.state == "Docked":
if signal == "thrust":
if self.fuel > 10:
self.speed = 1
self.change_state("Cruising")
elif self.state == "Cruising":
if signal == "thrust":
if self.speed < self.speed_max:
self.speed += 1
elif signal == "brake":
if self.speed > 0:
self.speed -= 1
elif signal == "dock":
if self.speed == 0:
self.change_state("Docked")
elif signal == "overdrive":
if self.fuel > 20:
self.speed = 3
self.heat += 30
elif self.state == "Overheating":
if signal == "brake":
self.speed = 0
def change_state(self, new_state):
self.state = new_state
self.state_time = 0.0
if self.state == "Overheating":
pass # Conceptual "WARNING" print on DSL
elif self.state == "Destroyed":
self.speed = 0
self.fuel = 0
def update(self, dt):
self.state_time += dt
if self.state == "Docked":
self.fuel += 5
self.heat -= 5
self.shields += 2
elif self.state == "Cruising":
if self.speed > 0:
self.fuel -= self.speed
self.heat += self.speed
if self.heat > 80:
self.change_state("Overheating")
else:
if self.fuel == 0:
self.speed = 0
self.change_state("Stranded")
else:
self.heat -= 1
elif self.state == "Overheating":
self.shields -= 5
self.heat -= 2
if self.shields == 0:
self.change_state("Destroyed")
else:
if self.heat < 50:
self.change_state("Cruising")
elif self.state == "Stranded":
self.heat -= 1
def render(self):
clear_screen()
print("=== SPACESHIP ANIMATION DSL SIMULATOR ===")
print("Controls: [W] Thrust | [S] Brake | [O] Overdrive | [D] Dock | [Q] Quit")
print("=========================================")
print(f"Current State: {self.state}")
print(f"Fuel: {self.fuel:3d}/{self.fuel_max} Heat: {self.heat:3d}/{self.heat_max}")
print(f"Shields: {self.shields:3d}/{self.shields_max} Speed: {self.speed}/{self.speed_max}")
print("=========================================")
print("-" * 40)
t_docked = " [=======] \n | DOCK | \n /| |\\ \n/_========= _\\"
t_idle = " ^ \n / \\ \n | O | \n /|___|\\ \n /_/ \\_\\ "
t_thrust_1 = " ^ \n / \\ \n | O | \n /|___|\\ \n /_/ W \\_\\ "
t_thrust_2 = " ^ \n / \\ \n | O | \n /|___|\\ \n /_/ w \\_\\ "
t_overheat = " ^ \n /!\\ \n | O | \n /|___|\\ \n /_/!!!\\_\\ "
t_destroyed = " \n \\ | / \n - X X - \n / | \\ \n "
anim_thrust = [t_thrust_1, t_thrust_1, t_thrust_2, t_thrust_2]
if self.state == "Docked":
print(t_docked)
elif self.state == "Cruising":
if self.speed == 0:
print(t_idle)
else:
print(anim_thrust[int(self.state_time * 10) % len(anim_thrust)])
elif self.state == "Overheating":
print(t_overheat)
elif self.state == "Stranded":
print(t_idle)
elif self.state == "Destroyed":
print(t_destroyed)
if self.state == "Overheating":
print("\n!!! WARNING: CRITICAL CORE TEMPERATURE !!!")
print("-" * 40)
def is_data():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
if __name__ == "__main__":
anim = SpaceshipAnimation()
old_settings = termios.tcgetattr(sys.stdin)
last_time = time.time()
try:
tty.setcbreak(sys.stdin.fileno())
while True:
current_time = time.time()
dt = current_time - last_time
last_time = current_time
if is_data():
c = sys.stdin.read(1).lower()
if c == 'q':
break
elif c in anim.input_map:
anim.send_signal(anim.input_map[c])
anim.update(dt)
anim.render()
time.sleep(0.1)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
clear_screen()
|
Es más de lo que nos piden para el trabajo y pinta bastante divertido de hacer. Iré actualizando con como queda.