Windmill
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 a1
. - 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; }