Windmill

From Esolang
Jump to navigation Jump to search
The Windmill logo

Windmill is a branchless programming language created by user:RainbowDash on 11/20/2024. Windmill supports all normal mathematical expressions such as 5*(2>1).

Windmill operates in a circular fashion, meaning that when the program reaches the end, it loops back to the starting point. You can define the starting point by marking it with a colon :. After the first loop, the program counter will jump to the line marked with the colon and continue from there.


Here’s an example of how you can use it to create a counter that counts up to 5.

This is the area where you can initialize variables.
i = 0
:This is where the program will start after the first loop
i = i+1
print i
halt = i==5

Variables

In Windmill, variables can only store numbers and are not case-sensitive.

Windmill also includes two special variables for increased functionality: IO and Halt.

  • When IO is set to 0, input and output instructions are disabled.
  • When Halt is set to 1, the program halts.


To initialize a variable, simply write the variable name followed by an assignment to a value.

a = 5
b = 5
c = a+b
print c
halt = 1

It is important to make a halting statement in your code or else it will run in an infinite loop forever.


Halting

Halting statements are what causes a program to stop running. These halting statements don’t have to be placed at the end of a program and there can be multiple of them.

number = 97
mod = 5
:modulus
io = number < mod
print number
halt = number < mod
number = number-mod

The program above which emulates the modulus operator, illustrates how the halting statement can be placed anywhere. This also shows how if the io variable is set to the same value as the halt variable it will only print when the program is done calculating.

IO

There are 2 instructions in windmill that act as inputs and outputs.

  • Print - Prints the value of a variable to the console. If the value is surrounded in quotes, it prints the value as a string instead of a number.
  • Prompt - Takes input from the user, if the input is a number it gets converted to a number. However, if the input is text it, the first letter gets converted to ASCII then stores it in the variable.


Here is a simple CAT program to demonstrate the use of these instructions that store the values in a variable A.

Prompt A
Print A
Halt = 1

Strings

You can store strings in variables, they come in the form of number arrays so you can read the value of each character.

a = "fish"
print a[0]
halt = 0

The expected result for the code above is 102. However if a happened to be a number it would return -1 because it's not a string. Thus, numbers don't have character indexes.


You can also prompt the user for strings and combine their response with other text when printing.

print "What is your name?"
prompt name
print "Hello, " name "!"
halt = 1

To concat strings is simple.

a = "a"
b = "b"
c = a "" b
print c
halt = 1

You are also able to change characters in a string. Characters are sorted in a 0-index.

string = "Pin"
string[1] = "a"
print string
halt = 1

The expected result is Pan. You can also change the expression in the brackets with variables.

Computational class

Windmill is Turing complete as it's equivalent to a family of general recursive functions and it is able to simulate all possible outcomes of a program.

Example programs

High low number guessing game

a = 76 
b = 12
c = 0
i = 0
starting = 0
gate = 1
reversegate = 0

:start

i = ((i+1)*gate) + i*reversegate
b = (b+(2 * a)*gate) + b*reversegate
c = (c+(a + b)*gate) + c*reversegate
a = ((5 + b) % 256*gate) + a*reversegate
b = (c % 256*gate) + b*reversegate
c = (0*gate) + c*reversegate

io = starting==0
print "Guess a random number, higher or lower"
starting = 1
io = 1

prompt x
higher = x>a
lower = x<a
equal = x==a

io = higher==1
print "Too high!"
gate = 1 - higher==1
reversegate = 1 - higher!=1
io = lower==1
print "Too low!"
gate = 1 - lower==1
reversegate = 1 - lower!=1
io = equal==1
print "Right on the money! Guess the next number."
gate = equal==1
reversegate = equal!=1
io = 1

halt = i>5

99 Bottles of beer.

bottles = 99
:start
io = bottles>1
    print bottles " bottles of beer on the wall,"
    print bottles " bottles of beer."
    print "Take one down, pass it around,"

io = bottles==1
    print "1 bottle of beer on the wall,"
    print "1 bottle of beer."
    print "Take one down, pass it around,"
    print "No bottles of beer on the wall."
    
bottles = bottles-1

halt = bottles==0

Password locked program. This program will only halt when you give it the right password.

password = "Windmill"
passwordlength = 8
isproccesing = 0

i = passwordlength
correctpass = 1

:start
correctpass = (1*(1-isproccesing))+(isproccesing*correctpass)

io = 1-isproccesing
    prompt guess
io = 1

i = i-1

isproccesing = (i)!==(-1)
correctpass = (guess[i]==password[i])*correctpass

io = correctpass*(i==0)
    print "Correct password!"
    
io = (1-correctpass)*(i==0)
    print "Incorrect password!"
    
i = ((1-isproccesing)*passwordlength)+(i*isproccesing)
halt = correctpass*(i==0)

To-do list program.

i = 0
a = " "
b = " "
c = " "
d = " "
e = " "
f = " "
:start
    print "To-Do list."
    print "1. " a
    print "2. " b
    print "3. " c
    print "4. " d
    print "5. " e
    print "6. " f
    print "What item in the list do you want to edit?"
    prompt val
    print "What do you want to enter in the registry?"
    prompt x
    
    io = 1
    current = x[i+1] 
    xi = x[i]
    rep = ((xi!=-1)*xi)+((xi==-1)*32)
    
    acur = a[i]
    astat = ((acur!=-1)*acur)+((acur==-1)*32)
    apros = ((astat!=-1)*astat)
    a[i] = ((val==1)*rep)+((val!=1)*apros)
    
    bcur = b[i]
    bstat = ((bcur!=-1)*bcur)+((bcur==-1)*32)
    bpros = ((bstat!=-1)*bstat)
    b[i] = ((val==2)*rep)+((val!=2)*bpros)
    
    ccur = c[i]
    cstat = ((ccur!=-1)*ccur)+((ccur==-1)*32)
    cpros = ((cstat!=-1)*cstat)
    c[i] = ((val==3)*rep)+((val!=3)*cpros)
    
    dcur = d[i]
    dstat = ((dcur!=-1)*dcur)+((dcur==-1)*32)
    dpros = ((dstat!=-1)*dstat)
    d[i] = ((val==4)*rep)+((val!=4)*dpros)
    
    ecur = e[i]
    estat = ((ecur!=-1)*ecur)+((ecur==-1)*32)
    epros = ((estat!=-1)*estat)
    e[i] = ((val==5)*rep)+((val!=5)*epros)
    
    fcur = f[i]
    fstat = ((fcur!=-1)*fcur)+((fcur==-1)*32)
    fpros = ((fstat!=-1)*fstat)
    f[i] = ((val==6)*rep)+((val!=6)*fpros)
    
    status = (current!=-1)
    
    alive = a[i+1]>2
    blive = b[i+1]>2
    clive = c[i+1]>2
    dlive = d[i+1]>2
    elive = e[i+1]>2
    flive = f[i+1]>2
    
    otherstatus = (status+alive+blive+clive+dlive+elive+flive) > 0
    i = ((i+1)*(otherstatus))
    
    io = 1-otherstatus
halt = 0

Implementation details

  • Strings should use normal 8 bit ASCII arrays.
  • Ignore indents, they are only there for the décor.
  • Since it is branchless, do not add any (if, while, for, etc) statements.
  • Equality statements return either a 0 or a 1.
  • If there is no starting point, go back to the first line of code.
  • Undefined values, such as reading from an uninitialized point within a string return -1.

Defined operators

In order to implement Windmill in a coding language you need to define these operators. Extra operators are allowed but not recommended.

  • + Addition
  • - Subtraction
  • / Division
  • * Multiplication
  • % Modulus
  • < Less than
  • > Greater than
  • <= Less than or equal
  • >= Greater than or equal
  • == Equal
  • != Not equal
  • ( ) Parentheses
  • [ ] Brackets - Get index of letter in string

JS implementation

function Windmill(prgm) {
  prgm = prgm.split('\n').map(line => line.replace(/\t/g, ' ').trim());

  const transformLine = line => {
    return line.replace(/(\b[a-zA-Z]+\b)|("[^"]*")/g, (match, variable, quotedString) => {
      if (quotedString) {
        return quotedString;
      }
      if (variable) {
        return `vars['${variable}']`;
      }
      return match; 
    });
  };

  const simpleEval = (expression, vars) => {
    expression = expression.match(/"[^"]*"|[^"]+/g).map(part => part.trim());
    let output
    for (let i = 0; i < expression.length; i++) {
        if (/^".*"$/.test(expression[i])) {
          let stringArray = [...expression[i].slice(1, -1)].map(char => char.charCodeAt(0));
          if(!Array.isArray(output)){
            output ? output = output.toString().split('').map(char => char.charCodeAt(0)) : output = []; 
          }
          output = output.concat(stringArray)
        } else {
          let evaluated = eval(expression[i].toLowerCase())
          if(Array.isArray(output)){
            if(!Array.isArray(evaluated)){
              output = output.concat(evaluated.toString().split('').map(char => char.charCodeAt(0)))
            } else {
                output = output.concat(eval(expression[i]))
            }
          } else {
            if(Array.isArray(evaluated)){
                output = evaluated
            } else {
                if(evaluated === undefined || Number.isNaN(evaluated)){
                    output = [45, 49]
                } else if(typeof evaluated == 'boolean'){
                    output = (+evaluated).toString().split('').map(char => char.charCodeAt(0))
                } else {
                    output = evaluated.toString().split('').map(char => char.charCodeAt(0))
                }
            }
          }
        }
    }
    return (String.fromCharCode(...output));
  };

  prgm = prgm.map(line => {
    if (line.trim().toLowerCase().startsWith("prompt")) {
      return line;
    }
    return line.replace(/^(\S+)(.*)/, (match, firstWord, restOfLine) => {
      return firstWord + transformLine(restOfLine);
    });
  });

  let vars = { io: 1, halt: 0 }; 
  let firstLoop = false;
  const checkpointIndex = prgm.findIndex(line => line.trim().startsWith(':'));

  while (vars['halt'] !== 1) { 
    for (let i = 0; i < prgm.length; i++) {
      if (i < checkpointIndex && firstLoop) {
        continue;
      }
      let args = prgm[i].split(" ");
      if (args.length > 1) {
        if (args[0].toLowerCase() === "print") {
          if (vars['io'] !== 0) {
            let expression = args.slice(1).join(' ');
            let output = simpleEval(expression,vars)
            console.log(output)
          }
        }  else if (args[0].toLowerCase() === "prompt") {
          if (vars['io'] !== 0) {
            let varName = args[1].toLowerCase();
            let userInput = prompt();
            if (!isNaN(userInput)) {
              vars[varName] = Number(userInput);
            } else {
              let stringArray = [...userInput].map(char => char.charCodeAt(0));
              vars[varName] = stringArray;
            }
          }
        } else {
          let index = args.indexOf("=");
          if (index !== -1) {
            let expression = args.slice(index + 1).join(' ');
             expression = expression.match(/"[^"]*"|[^"]+/g).map(part => part.trim());
            let output;
            for (let i = 0; i < expression.length; i++) {
                if (/^".*"$/.test(expression[i])) {
                  let stringArray = [...expression[i].slice(1, -1)].map(char => char.charCodeAt(0));
                  if(!Array.isArray(output)){
                    output ? output = output.toString().split('').map(char => char.charCodeAt(0)) : output = []; 
                  }
                  output = output.concat(stringArray);
                } else {
                  let evaluated = eval(expression[i].toLowerCase());
                  if (Array.isArray(output)) {
                    if (!Array.isArray(evaluated)) {
                      output = output.concat(evaluated.toString().split('').map(char => char.charCodeAt(0)));
                    } else {
                        output = output.concat(eval(expression[i]));
                    }
                  } else {
                    if (evaluated === undefined || Number.isNaN(evaluated)) {
                        output = -1;
                    } else {
                        if (typeof evaluated == 'boolean') {
                            output = +evaluated;
                        } else {
                            output = evaluated;
                        }
                    }
                  }
                }
            }
            let s = args[0].toLowerCase();
            let match = s.match(/(\w+)\[(.*?)\]/);
            if (match) {
                let expBeforeBracket = match[1];
                let expInBracket = match[2];    
                if (isNaN(expInBracket)) {
                    let evaled = simpleEval(transformLine(expInBracket), vars);
                    vars[expBeforeBracket][evaled] = output;
                } else {
                    vars[expBeforeBracket][expInBracket] = output;
                }
            } else {
                vars[s] = output;
            }
          }
        }
      }
    }
    firstLoop = true;
  }
  return vars;
}