parent
14fe23f780
commit
7706b95125
@ -0,0 +1,173 @@
|
||||
REM This is an example of a more complex program,
|
||||
REM which spawns procedural waves
|
||||
|
||||
LET wave = 0
|
||||
LET timeout = @time + 120000
|
||||
|
||||
initial_wait:
|
||||
LET remaining = timeout - @time
|
||||
IF remaining <= 0 THEN
|
||||
GOTO main
|
||||
END IF
|
||||
|
||||
PRINT "[red]Enemies[white] approaching: "
|
||||
PRINT ceil(remaining / 1000), " s"
|
||||
PRINT_MESSAGE_MISSION()
|
||||
|
||||
wait(0.5)
|
||||
|
||||
GOTO initial_wait
|
||||
main:
|
||||
wave = wave + 1
|
||||
REM TODO: difficult control wave multiplier
|
||||
LET progression = POW(wave / 4, 0.75)
|
||||
REM TODO: optimize duplicate operations
|
||||
progression = MIN(progression / 2 + rand(progression / 2), 10)
|
||||
|
||||
LET units = 2 + SQRT(progression) * 4 + RAND(progression * 2)
|
||||
REM TODO: difficulty control unit amount
|
||||
units = CEIL(units * 1)
|
||||
LET tank_units = FLOOR(RAND(units))
|
||||
LET mech_units = FLOOR(RAND(units - tank_units))
|
||||
LET air_units = units - tank_units - mech_units
|
||||
|
||||
LET spawnx = 30
|
||||
LET spawny = 50
|
||||
|
||||
GOTO spawn_tank
|
||||
spawn_tank_end:
|
||||
GOTO spawn_mech
|
||||
spawn_mech_end:
|
||||
LET spawnx = 20
|
||||
GOTO spawn_air
|
||||
spawn_air_end:
|
||||
|
||||
WRITE(wave, cell1, 1)
|
||||
READ(timeout, cell1, 0)
|
||||
timeout = @time + timeout * 1000
|
||||
|
||||
main_wait:
|
||||
LET remaining = timeout - @time
|
||||
IF remaining <= 0 THEN
|
||||
GOTO main
|
||||
END IF
|
||||
|
||||
PRINT "[yellow]Wave ", wave, "[white] - "
|
||||
PRINT "Next wave: ", ceil(remaining / 1000), " s"
|
||||
PRINT_MESSAGE_MISSION()
|
||||
|
||||
wait(0.5)
|
||||
|
||||
GOTO main_wait
|
||||
|
||||
spawn_tank:
|
||||
LET spawned = 0
|
||||
spawn_tank_loop:
|
||||
LET roll = rand(progression)
|
||||
IF roll >= 3 THEN
|
||||
IF roll >= 4 THEN
|
||||
SPAWN(@conquer, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 5.75
|
||||
ELSE
|
||||
SPAWN(@vanquish, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 3.5
|
||||
END IF
|
||||
ELSE
|
||||
IF roll >= 2 THEN
|
||||
SPAWN(@precept, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 3.25
|
||||
ELSE
|
||||
REM Small units can unclump easily
|
||||
IF roll >= 1 THEN
|
||||
SPAWN(@locus, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 1.0
|
||||
ELSE
|
||||
SPAWN(@stell, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 1.0
|
||||
END IF
|
||||
END IF
|
||||
END IF
|
||||
|
||||
IF spawnx < 10 THEN
|
||||
spawnx = 10
|
||||
END IF
|
||||
|
||||
spawned = spawned + 1
|
||||
IF spawned < tank_units THEN
|
||||
GOTO spawn_tank_loop
|
||||
END IF
|
||||
GOTO spawn_tank_end
|
||||
|
||||
spawn_mech:
|
||||
LET spawned = 0
|
||||
spawn_mech_loop:
|
||||
LET roll = rand(progression)
|
||||
IF roll >= 3 THEN
|
||||
IF roll >= 4 THEN
|
||||
SPAWN(@collaris, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 5.5
|
||||
ELSE
|
||||
SPAWN(@tecta, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 2.87
|
||||
END IF
|
||||
ELSE
|
||||
IF roll >= 2 THEN
|
||||
SPAWN(@anthicus, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 2.62
|
||||
ELSE
|
||||
IF roll >= 1 THEN
|
||||
SPAWN(@cleroi, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 1.0
|
||||
ELSE
|
||||
SPAWN(@merui, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 1.0
|
||||
END IF
|
||||
END IF
|
||||
END IF
|
||||
|
||||
IF spawnx < 10 THEN
|
||||
spawnx = 10
|
||||
END IF
|
||||
|
||||
spawned = spawned + 1
|
||||
IF spawned < mech_units THEN
|
||||
GOTO spawn_mech_loop
|
||||
END IF
|
||||
GOTO spawn_mech_end
|
||||
|
||||
spawn_air:
|
||||
LET spawned = 0
|
||||
spawn_air_loop:
|
||||
LET roll = rand(progression)
|
||||
IF roll >= 3 THEN
|
||||
IF roll >= 4 THEN
|
||||
SPAWN(@disrupt, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 5.75
|
||||
ELSE
|
||||
SPAWN(@quell, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 4.5
|
||||
END IF
|
||||
ELSE
|
||||
IF roll >= 2 THEN
|
||||
SPAWN(@obviate, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 3.12
|
||||
ELSE
|
||||
IF roll >= 1 THEN
|
||||
SPAWN(@avert, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 1
|
||||
ELSE
|
||||
SPAWN(@elude, spawnx, spawny, 0, @crux, _)
|
||||
spawnx = spawnx - 1
|
||||
END IF
|
||||
END IF
|
||||
END IF
|
||||
|
||||
IF spawnx < 10 THEN
|
||||
spawnx = 10
|
||||
END IF
|
||||
|
||||
spawned = spawned + 1
|
||||
IF spawned < air_units THEN
|
||||
GOTO spawn_air_loop
|
||||
END IF
|
||||
GOTO spawn_air_end
|
@ -0,0 +1,127 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
RShift,
|
||||
LShift,
|
||||
Gt,
|
||||
Lt,
|
||||
Gte,
|
||||
Lte,
|
||||
Eq,
|
||||
Neq,
|
||||
Max,
|
||||
Min,
|
||||
Pow,
|
||||
// etc.
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub(crate) fn precedence(self) -> u8 {
|
||||
use Operator as O;
|
||||
match self {
|
||||
O::Add | O::Sub => 3,
|
||||
O::RShift | O::LShift => 4,
|
||||
O::Mod => 5,
|
||||
O::Mul | O::Div => 10,
|
||||
O::Eq | O::Neq | O::Gt | O::Lt | O::Gte | O::Lte => 0,
|
||||
_ => 128,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_fn_name(raw: &str) -> Option<Self> {
|
||||
match raw {
|
||||
"max" => Some(Self::Max),
|
||||
"min" => Some(Self::Min),
|
||||
"pow" => Some(Self::Pow),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn format_operator(operator: Operator) -> &'static str {
|
||||
match operator {
|
||||
Operator::Eq => "equal",
|
||||
Operator::Neq => "notEqual",
|
||||
Operator::Lt => "lessThan",
|
||||
Operator::Lte => "lessThanEq",
|
||||
Operator::Gt => "greaterThan",
|
||||
Operator::Gte => "greaterThanEq",
|
||||
Operator::Add => "add",
|
||||
Operator::Sub => "sub",
|
||||
Operator::Mul => "mul",
|
||||
Operator::Div => "div",
|
||||
Operator::Mod => "mod",
|
||||
Operator::RShift => "shr",
|
||||
Operator::LShift => "shl",
|
||||
Operator::Max => "max",
|
||||
Operator::Min => "min",
|
||||
Operator::Pow => "pow",
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum UnaryOperator {
|
||||
Floor,
|
||||
Round,
|
||||
Ceil,
|
||||
Rand,
|
||||
Sqrt,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for UnaryOperator {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
"floor" => Ok(Self::Floor),
|
||||
"round" => Ok(Self::Round),
|
||||
"ceil" => Ok(Self::Ceil),
|
||||
"rand" => Ok(Self::Rand),
|
||||
"sqrt" => Ok(Self::Sqrt),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn format_unary_operator(operator: UnaryOperator) -> &'static str {
|
||||
match operator {
|
||||
UnaryOperator::Floor => "floor",
|
||||
UnaryOperator::Round => "round",
|
||||
UnaryOperator::Ceil => "ceil",
|
||||
UnaryOperator::Rand => "rand",
|
||||
UnaryOperator::Sqrt => "sqrt",
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub builtin_functions: HashMap<String, (String, usize)>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
macro_rules! builtin_function {
|
||||
( $name:expr, $target_name:expr, $n_args:expr ) => {
|
||||
(String::from($name), (String::from($target_name), $n_args))
|
||||
};
|
||||
}
|
||||
Self {
|
||||
builtin_functions: HashMap::from([
|
||||
builtin_function!("print_flush", "printflush", 1),
|
||||
builtin_function!("print_message_mission", "message mission", 0),
|
||||
builtin_function!("read", "read", 3),
|
||||
builtin_function!("write", "write", 3),
|
||||
builtin_function!("wait", "wait", 1),
|
||||
builtin_function!("set_flag", "setflag", 2),
|
||||
builtin_function!("get_flag", "getflag", 2),
|
||||
builtin_function!("spawn", "spawn", 6),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod common;
|
||||
pub mod compile;
|
||||
pub mod cursor;
|
||||
pub mod parse;
|
||||
|
@ -0,0 +1,35 @@
|
||||
use std::path::Path;
|
||||
|
||||
use basic_to_mindustry::common::Config;
|
||||
use basic_to_mindustry::compile::{optimize_set_use, translate_ast};
|
||||
use basic_to_mindustry::parse::{build_ast, tokenize};
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
let config = Config::default();
|
||||
for entry in Path::new("./examples/").read_dir().unwrap() {
|
||||
let Ok(entry) = entry else { continue };
|
||||
if entry
|
||||
.file_name()
|
||||
.into_string()
|
||||
.map(|name| name.ends_with(".basic"))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let file_name = entry.file_name().into_string().unwrap();
|
||||
let file = std::fs::read_to_string(entry.path()).unwrap_or_else(|e| {
|
||||
panic!("Error opening {:?}: {:?}", file_name, e);
|
||||
});
|
||||
|
||||
let tokenized = tokenize(&file).unwrap_or_else(|e| {
|
||||
panic!("Error tokenizing {:?}: {:?}", file_name, e);
|
||||
});
|
||||
let parsed = build_ast(&tokenized, &config).unwrap_or_else(|e| {
|
||||
panic!("Error parsing {:?}: {:?}", file_name, e);
|
||||
});
|
||||
let translated = translate_ast(&parsed, &mut Default::default(), &config);
|
||||
let optimized = optimize_set_use(translated);
|
||||
|
||||
let _ = optimized;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue