├── ConstLib.py ├── LICENSE ├── MineFriff Interpreter.zip ├── MineFriff.py ├── README.md └── main.py /ConstLib.py: -------------------------------------------------------------------------------- 1 | LEFT = (-1, 0, 0) 2 | RIGHT = (1, 0, 0) 3 | UP = (0, -1, 0) 4 | DOWN = (0, 1, 0) 5 | UP_LAYER = (0, 0, 1) 6 | DOWN_LAYER = (0, 0, -1) 7 | 8 | COMMAND_LEFT = "<" 9 | COMMAND_RIGHT = ">" 10 | COMMAND_UP = "^" 11 | COMMAND_DOWN = "v" 12 | 13 | OUTPUT = "o" 14 | LITERAL = "0123456789abcdef" 15 | REVERSE = "r" 16 | PUSH = "," 17 | POP = "." 18 | DELETE = "!" 19 | LENGTH = "l" 20 | EQUALS = "=" 21 | DUPLICATE = ":" 22 | SWAP_TOP = "$" 23 | TRAMPOLINE = "#" 24 | CONDITIONAL = "?" 25 | END_PROG = ";" 26 | GREATER_THAN = ")" 27 | LESS_THAN = "(" 28 | MULTIPLY = "*" 29 | DIVIDE = "/" 30 | ADD = "+" 31 | SUBTRACT = "-" 32 | RANDOM = "x" 33 | RETURN = "~" 34 | PUSH_CHR = "C" 35 | PUSH_INT = "I" 36 | PUSH_FLOAT = "F" 37 | NEXT_LINE = "`" 38 | R_SHIFT = "]" 39 | L_SHIFT = "[" 40 | INPUT = "i" 41 | MODULUS = "%" 42 | JUMP = '"' 43 | COMMENT_START = "{" 44 | COMMENT_END = "}" 45 | PEAK_UP = "U" 46 | PEAK_DOWN = "D" 47 | PEAK_LEFT = "L" 48 | PEAK_RIGHT = "R" 49 | PRINT = "p" 50 | 51 | class PEAK_DIR: 52 | UP = (0, -1, 0) 53 | DOWN = (0, 1, 0) 54 | LEFT = (-1, 0, 0) 55 | RIGHT = (1, 0, 0) 56 | NONE = (0, 0, 0) 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JonoCode9374 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MineFriff Interpreter.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyxal/Minefriff/f94364f458893e342df5e2166efc19bdc499315b/MineFriff Interpreter.zip -------------------------------------------------------------------------------- /MineFriff.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import mcpi.minecraft as minecraft 3 | import mcpi.block as block 4 | import mcpi.entity as entity 5 | import time, random, sys, os 6 | 7 | def getSavePath(directory, extension): 8 | if int(sys.version[0]) < 3: 9 | from tkFileDialog import asksaveasfilename 10 | from Tkinter import Tk 11 | else: 12 | from tkinter.filedialog import asksaveasfilename 13 | from tkinter import Tk 14 | master = Tk() 15 | master.attributes("-topmost", True) 16 | path = asksaveasfilename(initialdir=directory,filetypes=['MineFriff {*.'+extension+'}'],defaultextension="."+extension,title="Save") 17 | master.destroy() 18 | return path 19 | 20 | def getLoadPath(directory, extension): 21 | if int(sys.version[0]) < 3: 22 | from tkFileDialog import askopenfilename 23 | from Tkinter import Tk 24 | else: 25 | from tkinter.filedialog import askopenfilename 26 | from tkinter import Tk 27 | master = Tk() 28 | master.attributes("-topmost", True) 29 | path = askopenfilename(initialdir=directory,filetypes=['MineFriff {*.'+extension+'}'],title="Open") 30 | master.destroy() 31 | return path 32 | 33 | stack = [] 34 | mc = minecraft.Minecraft() 35 | 36 | LEFT = (0, 0, -1) 37 | RIGHT = (0, 0, 1) 38 | UP = (1, 0, 0) 39 | DOWN = (-1, 0, 0) 40 | LAYER_UP = (0, 1, 0) 41 | LAYER_DOWN = (0, -1, 0) 42 | 43 | direction = RIGHT 44 | temp_reg = 0 45 | push_mode = int 46 | next_op = "+" 47 | 48 | def change_direction(new_dir): 49 | if new_dir == direction: 50 | return direction 51 | 52 | elif new_dir == LEFT: 53 | return LEFT 54 | 55 | elif new_dir == RIGHT: 56 | return RIGHT 57 | 58 | elif new_dir == DOWN: 59 | return DOWN 60 | 61 | elif new_dir == UP: 62 | return UP 63 | 64 | elif new_dir == LAYER_UP: 65 | return LAYER_UP 66 | 67 | elif new_dir == LAYER_DOWN: 68 | return LAYER_DOWN 69 | 70 | else: 71 | raise Exception("Unknown direction") 72 | 73 | def next_block(ip): 74 | return (ip[0] + direction[0], ip[1] + direction[1], ip[2] + direction[2]) 75 | 76 | def main(start, end): 77 | global direction, stack, temp_reg, push_mode, next_op 78 | ip = start 79 | output = "" 80 | eval_block = mc.getBlockWithNBT(ip[0], ip[1], ip[2]) 81 | if eval_block == block.GLASS.id: 82 | return 83 | 84 | while eval_block != block.GLASS.id: 85 | 86 | if eval_block.id == block.WOOD_PLANKS.id: 87 | if eval_block.data == block.WOOD_PLANKS_OAK.data: 88 | direction = change_direction(LEFT) 89 | 90 | elif eval_block.data == block.WOOD_PLANKS_SPRUCE.data: 91 | direction = change_direction(RIGHT) 92 | 93 | elif eval_block.data == block.WOOD_PLANKS_BIRCH.data: 94 | direction = change_direction(UP) 95 | 96 | elif eval_block.data == block.WOOD_PLANKS_JUNGLE.data: 97 | direction = change_direction(DOWN) 98 | 99 | elif eval_block.data == block.WOOD_PLANKS_DARK_OAK.data: 100 | direction = change_direction(LAYER_UP) 101 | 102 | elif eval_block.data == block.WOOD_PLANKS_ACACIA.data: 103 | direction = change_direction(LAYER_DOWN) 104 | 105 | elif eval_block.id == block.WOOL.id: 106 | if next_op == "*": 107 | temp_reg *= eval_block.data 108 | 109 | elif next_op == "+": 110 | temp_reg += eval_block.data 111 | 112 | elif next_op == "/": 113 | temp_reg /= float(eval_block.data) 114 | 115 | elif next_op == "-": 116 | temp_reg -= eval_block.data 117 | 118 | next_op = "+" 119 | 120 | 121 | elif eval_block.id == block.PISTON.id: 122 | stack.append(push_mode(temp_reg)) 123 | temp_reg = 0 124 | 125 | 126 | elif eval_block.id == block.AIR.id: 127 | pass 128 | 129 | elif eval_block.id == block.WOOD.id: 130 | if eval_block.data == block.BIRCH_WOOD.data: 131 | push_mode = chr 132 | 133 | elif eval_block.data == block.JUNGLE_WOOD.data: 134 | ip = (ip[0] + (direction[0] * (start[0] - end[0])), ip[1] + (direction[1] * (start[1] - end[1])), 135 | ip[2] + (direction[2] * (start[2] - end[2]))) 136 | eval_block = mc.getBlockWithNBT(ip[0], ip[1], ip[2]) 137 | continue 138 | 139 | elif eval_block.id == block.ICE_PACKED.id: 140 | push_mode = int 141 | 142 | elif eval_block.id == block.FURNACE_INACTIVE.id: 143 | push_mode = float 144 | 145 | elif eval_block.id == block.SPONGE.id: 146 | print(stack[-1], end="") 147 | del stack[-1] 148 | 149 | elif eval_block.id == block.HOPPER.id: 150 | stack.append(stack[-1]) 151 | 152 | elif eval_block.id == block.NETHERRACK.id: 153 | stack = stack[::-1] 154 | 155 | elif eval_block.id == block.STONE.id: 156 | if eval_block.data == block.STONE.data: 157 | stack.append(len(stack)) 158 | elif eval_block.data == block.STONE_DIORITE.data: 159 | if temp_reg == 0: 160 | #Stack operations 161 | x, y = stack[-1], stack[-2] 162 | if str not in [type(x), type(y)]: 163 | del stack[-1] 164 | del stack[-1] 165 | stack.append(y / x) 166 | 167 | else: 168 | raise Exception("Different types for /") 169 | 170 | else: 171 | next_op = "/" 172 | 173 | elif eval_block.data == block.STONE_ANDESITE.data: 174 | if temp_reg == 0: 175 | #Stack operations 176 | x, y = stack[-1], stack[-2] 177 | if str not in [type(x), type(y)]: 178 | del stack[-1] 179 | del stack[-1] 180 | stack.append(x + y) 181 | 182 | else: 183 | raise Exception("Different types for +") 184 | 185 | else: 186 | next_op = "+" 187 | 188 | elif eval_block.data == block.STONE_GRANITE.data: 189 | if temp_reg == 0: 190 | #Stack operations 191 | x, y = stack[-1], stack[-2] 192 | if str not in [type(x), type(y)]: 193 | del stack[-1] 194 | del stack[-1] 195 | stack.append(y - x) 196 | 197 | else: 198 | raise Exception("Different types for -") 199 | 200 | else: 201 | next_op = "-" 202 | 203 | elif eval_block.id == block.SAND.id: 204 | if temp_reg == 0: 205 | #Stack operations 206 | x, y = stack[-1], stack[-2] 207 | if str not in [type(x), type(y)]: 208 | del stack[-1] 209 | del stack[-1] 210 | stack.append(y % x) 211 | 212 | else: 213 | raise Exception("Different types for %") 214 | 215 | else: 216 | next_op = "%" 217 | 218 | 219 | elif eval_block.id == block.REDSTONE_LAMP_INACTIVE.id: 220 | test = stack[-1] 221 | del stack[-1] 222 | 223 | if test == 0: 224 | ip = next_block(ip) 225 | ip = next_block(ip) 226 | eval_block = mc.getBlockWithNBT(ip[0], ip[1], ip[2]) 227 | continue 228 | 229 | 230 | elif eval_block.id == block.SLIME_BLOCK.id: 231 | ip = next_block(ip) 232 | ip = next_block(ip) 233 | eval_block = mc.getBlockWithNBT(ip[0], ip[1], ip[2]) 234 | continue 235 | 236 | elif eval_block.id == block.HAY_BLOCK.id: 237 | ip = (ip[0], ip[1], start[2]) 238 | eval_block = mc.getBlockWithNBT(ip[0], ip[1], ip[2]) 239 | direction = change_direction(RIGHT) 240 | continue 241 | 242 | elif eval_block.id == block.BEDROCK.id: 243 | if len(stack) < 2: 244 | raise Exception("Not enough values for comparison") 245 | x, y = stack[-1], stack[-2] 246 | del stack[-1] 247 | del stack[-1] 248 | 249 | if x == y: 250 | stack.append(1) 251 | else: 252 | stack.append(0) 253 | 254 | elif eval_block.id == block.DISPENSER.id: 255 | direction = change_direction(random.choice([LEFT, RIGHT, UP, DOWN])) 256 | 257 | elif eval_block.id == block.DIAMOND_BLOCK.id: 258 | if len(stack) < 2: 259 | raise Exception("Not enough values for comparison") 260 | x, y = stack[-1], stack[-2] 261 | del stack[-1] 262 | del stack[-1] 263 | 264 | if x > y: 265 | stack.append(1) 266 | else: 267 | stack.append(0) 268 | 269 | elif eval_block.id == block.COAL_BLOCK.id: 270 | if len(stack) < 2: 271 | raise Exception("Not enough values for comparison") 272 | x, y = stack[-1], stack[-2] 273 | del stack[-1] 274 | del stack[-1] 275 | 276 | if x < y: 277 | stack.append(1) 278 | else: 279 | stack.append(0) 280 | 281 | elif eval_block.id == block.DIRT.id: 282 | if eval_block.data == block.DIRT.data: 283 | val = stack[-1] 284 | del stack[-1] 285 | stack.insert(0, val) 286 | 287 | elif eval_block.data == block.DIRT_PODZOL.data: 288 | if temp_reg == 0: 289 | #Stack operations 290 | x, y = stack[-1], stack[-2] 291 | if str not in [type(x), type(y)]: 292 | del stack[-1] 293 | del stack[-1] 294 | stack.append(x * y) 295 | 296 | else: 297 | raise Exception("Different types for *") 298 | 299 | else: 300 | next_op = "*" 301 | 302 | elif eval_block.id == block.COBBLESTONE.id: 303 | val = stack[0] 304 | del stack[0] 305 | stack.append(val) 306 | 307 | 308 | elif eval_block.id == block.END_BRICKS.id: 309 | ip = (ip[0] - 1, ip[1], start[2]) 310 | eval_block = mc.getBlockWithNBT(ip[0], ip[1], ip[2]) 311 | continue 312 | 313 | elif eval_block.id == block.OBSIDIAN.id: 314 | stack[-1], stack[-2] = stack[-2], stack[-1] 315 | ip = next_block(ip) 316 | eval_block = mc.getBlockWithNBT(ip[0], ip[1], ip[2]) 317 | 318 | def export(): 319 | start = [169, 56, -596] 320 | end = [69, 56, -496] 321 | code = "" 322 | 323 | for x in range(start[0], end[0], -1): 324 | y = start[1] 325 | for z in range(start[2], end[2], 1): 326 | eval_block = mc.getBlockWithNBT(x, y, z) 327 | 328 | if eval_block.id == block.WOOD_PLANKS.id: #Direction change 329 | if eval_block.data == block.WOOD_PLANKS_OAK.data: #Go left 330 | code += "<" 331 | 332 | elif eval_block.data == block.WOOD_PLANKS_SPRUCE.data: #Go right 333 | code += ">" 334 | 335 | elif eval_block.data == block.WOOD_PLANKS_BIRCH.data: #Go up 336 | code += "^" 337 | 338 | elif eval_block.data == block.WOOD_PLANKS_JUNGLE.data: #Go down 339 | code += "v" 340 | 341 | elif eval_block.data == block.WOOD_PLANKS_DARK_OAK.data: 342 | code += "!" 343 | 344 | elif eval_block.data == block.WOOD_PLANKS_ACACIA.data: 345 | code += "@" 346 | 347 | elif eval_block.id == block.WOOL.id: 348 | code += "0123456789abcdef"[eval_block.data] 349 | 350 | elif eval_block.id == block.PISTON.id: 351 | code += "," 352 | 353 | 354 | elif eval_block.id == block.AIR.id: 355 | code += " " 356 | 357 | elif eval_block.id == block.WOOD.id: 358 | if eval_block.data == block.BIRCH_WOOD.data: 359 | code += "C" 360 | 361 | elif eval_block.data == block.JUNGLE_WOOD.data: 362 | pass 363 | 364 | 365 | elif eval_block.id == block.ICE_PACKED.id: 366 | code += "I" 367 | 368 | elif eval_block.id == block.FURNACE_INACTIVE.id: 369 | code += "F" 370 | 371 | elif eval_block.id == block.SPONGE.id: 372 | code += "o" 373 | 374 | elif eval_block.id == block.HOPPER.id: 375 | code += ":" 376 | 377 | elif eval_block.id == block.NETHERRACK.id: 378 | code += "r" 379 | 380 | elif eval_block.id == block.STONE.id: 381 | if eval_block.data == block.STONE.data: 382 | code += "l" 383 | elif eval_block.data == block.STONE_DIORITE.data: 384 | code += "/" 385 | 386 | elif eval_block.data == block.STONE_ANDESITE.data: 387 | code += "+" 388 | 389 | elif eval_block.data == block.STONE_GRANITE.data: 390 | code += "-" 391 | 392 | elif eval_block.id == block.SAND.id: 393 | code += "%" 394 | 395 | 396 | elif eval_block.id == block.REDSTONE_LAMP_INACTIVE.id: 397 | code += "?" 398 | 399 | 400 | elif eval_block.id == block.SLIME_BLOCK.id: 401 | code += "#" 402 | 403 | elif eval_block.id == block.HAY_BLOCK.id: 404 | code += "~" 405 | 406 | elif eval_block.id == block.GLASS.id: 407 | code += ";" 408 | 409 | elif eval_block.id == block.BEDROCK.id: 410 | code += "=" 411 | 412 | elif eval_block.id == block.DISPENSER.id: 413 | code += "x" 414 | 415 | elif eval_block.id == block.DIAMOND_BLOCK.id: 416 | code += ")" 417 | 418 | elif eval_block.id == block.COAL_BLOCK.id: 419 | code += "(" 420 | 421 | elif eval_block.id == block.DIRT.id: 422 | if eval_block.data == block.DIRT.data: 423 | code += "]" 424 | 425 | elif eval_block.data == block.DIRT_PODZOL.data: 426 | code += "*" 427 | 428 | elif eval_block.id == block.COBBLESTONE.id: 429 | code += "[" 430 | 431 | 432 | elif eval_block.id == block.END_BRICKS.id: 433 | code += "`" 434 | 435 | elif eval_block.id == block.OBSIDIAN.id: 436 | code += "$" 437 | code = code.rstrip() 438 | code += "\n" 439 | 440 | 441 | path = getSavePath("", "mf") 442 | if not path: 443 | mc.postToChat('Canceled') 444 | return 445 | f = open(path, "w") 446 | f.write(code) 447 | f.close() 448 | mc.postToChat('Code saved in '+path) 449 | 450 | def _import(): 451 | path = getLoadPath("", "mf") 452 | if not path: 453 | mc.postToChat('Canceled') 454 | return 455 | f = open(path) 456 | code = f.read() 457 | f.close() 458 | start = [169, 56, -596] 459 | loc = [169, 56, -596] 460 | end = [69, 56, -496] 461 | for letter in code: 462 | if letter == "<": 463 | mc.setBlock(loc[0], loc[1], loc[2], block.WOOD_PLANKS_OAK) 464 | 465 | elif letter == ">": 466 | mc.setBlock(loc[0], loc[1], loc[2], block.WOOD_PLANKS_SPRUCE) 467 | 468 | elif letter == "^": 469 | mc.setBlock(loc[0], loc[1], loc[2], block.WOOD_PLANKS_BIRCH) 470 | 471 | elif letter == "v": 472 | mc.setBlock(loc[0], loc[1], loc[2], block.WOOD_PLANKS_JUNGLE) 473 | 474 | elif letter == "o": 475 | mc.setBlock(loc[0], loc[1], loc[2], block.SPONGE) 476 | 477 | elif letter in "0123456789abcdef": 478 | mc.setBlock(loc[0], loc[1], loc[2], block.Block(block.WOOL.id, 479 | "0123456789abcdef".index(letter))) 480 | 481 | elif letter == "r": 482 | mc.setBlock(loc[0], loc[1], loc[2], block.NETHERRACK) 483 | 484 | elif letter == ",": 485 | mc.setBlock(loc[0], loc[1], loc[2], block.PISTON) 486 | 487 | elif letter == ".": 488 | mc.setBlock(loc[0], loc[1], loc[2], block.Block(29)) 489 | 490 | elif letter == "l": 491 | mc.setBlock(loc[0], loc[1], loc[2], block.STONE) 492 | 493 | elif letter == "=": 494 | mc.setBlock(loc[0], loc[1], loc[2], block.BEDROCK) 495 | 496 | elif letter == ":": 497 | mc.setBlock(loc[0], loc[1], loc[2], block.HOPPER) 498 | 499 | elif letter == "$": 500 | mc.setBlock(loc[0], loc[1], loc[2], block.OBSIDIAN) 501 | 502 | elif letter == "#": 503 | mc.setBlock(loc[0], loc[1], loc[2], block.SLIME_BLOCK) 504 | 505 | elif letter == "?": 506 | mc.setBlock(loc[0], loc[1], loc[2], block.REDSTONE_LAMP_INACTIVE) 507 | 508 | elif letter == ";": 509 | mc.setBlock(loc[0], loc[1], loc[2], block.GLASS) 510 | 511 | elif letter == ")": 512 | mc.setBlock(loc[0], loc[1], loc[2], block.DIAMOND_BLOCK) 513 | 514 | elif letter == "(": 515 | mc.setBlock(loc[0], loc[1], loc[2], block.COAL_BLOCK) 516 | 517 | elif letter == "*": 518 | mc.setBlock(loc[0], loc[1], loc[2], block.DIRT_PODZOL) 519 | 520 | elif letter == "/": 521 | mc.setBlock(loc[0], loc[1], loc[2], block.STONE_DIORITE) 522 | 523 | elif letter == "+": 524 | mc.setBlock(loc[0], loc[1], loc[2], block.STONE_ANDESITE) 525 | 526 | elif letter == "-": 527 | mc.setBlock(loc[0], loc[1], loc[2], block.STONE_GRANITE) 528 | 529 | elif letter == "x": 530 | mc.setBlock(loc[0], loc[1], loc[2], block.DISPENSER) 531 | 532 | elif letter == "~": 533 | mc.setBlock(loc[0], loc[1], loc[2], block.HAY_BLOCK) 534 | 535 | elif letter == "I": 536 | mc.setBlock(loc[0], loc[1], loc[2], block.ICE_PACKED) 537 | 538 | elif letter == "C": 539 | mc.setBlock(loc[0], loc[1], loc[2], block.BIRCH_WOOD) 540 | 541 | elif letter == "F": 542 | mc.setBlock(loc[0], loc[1], loc[2], block.FURNACE_INACTIVE) 543 | 544 | elif letter == "`": 545 | mc.setBlock(loc[0], loc[1], loc[2], block.END_BRICKS) 546 | 547 | elif letter == "]": 548 | mc.setBlock(loc[0], loc[1], loc[2], block.DIRT) 549 | 550 | elif letter == "[": 551 | mc.setBlock(loc[0], loc[1], loc[2], block.COBBLESTONE) 552 | 553 | elif letter == "!": 554 | mc.setBlock(loc[0], loc[1], loc[2], block.WOOD_PLANKS_ACACIA) 555 | 556 | elif letter == "@": 557 | mc.setBlock(loc[0], loc[1], loc[2], block.WOOD_PLANKS_DARK_OAK) 558 | 559 | elif letter == "%": 560 | mc.setBlock(loc[0], loc[1], loc[2], block.SAND) 561 | 562 | if loc[2] == end[2] or letter == "\n": 563 | loc[2] = start[2] 564 | loc[0] -= 1 565 | else: 566 | loc[2] += 1 567 | 568 | 569 | 570 | if __name__ == "__main__": 571 | player_pos = mc.player.getPos() 572 | if len(sys.argv) > 1: 573 | if sys.argv[1] == "e": 574 | export() 575 | 576 | elif sys.argv[1] == "i": 577 | _import() 578 | 579 | elif sys.argv[1] == "s": 580 | main((169, 56, -596), (69, 56, -497)) 581 | else: 582 | main((player_pos.x, player_pos.y - 1, player_pos.z), (player_pos.x - 100, player_pos.y, 583 | player_pos.z + 100)) 584 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MineFriff - A Blocky Esolang 2 | **MineFriff** brings the joy of traditional esolangs such as `Befunge` and `><>` into the blocky pixelated world of _Minecraft_. Much like fungeoid languages, MineFriff allows code to be set out in a 2D code area, which can range from a 100*100 grid (allowing for 10,000 instructions) to any size of square (or cube). 3 | 4 | As aforementioned, MineFriff allows for free-form code to be written in any size cube (meaning that 3D code is allowed). 5 | 6 | **MineFriff** comes in three flavours: Strict, Freeform and Textual. _Strict_ MineFriff is contained in a 100 by 100 block grid, and can be exported to a `.mf` file. However, strict MineFriff is only avaliable in the offical MineFriff interpreter world. _Freeform_ MineFriff is avaliable in any world, but is unexportable to an actual file -- code is run using. 7 | ``` 8 | /py MineFriff 9 | ``` 10 | 11 | _Textual_ MineFriff is a text based ascii version of the blocky esolang, which is avaliable to everyone -- all one needs is the python based MineFriff interpreter. 12 | 13 | ## Concepts 14 | ### Code Box 15 | Like most fungeoidal esolangs, MineFriff has a code box in which the program is placed. In strict MineFriff, this code box is as shown: 16 | 17 | ![100 * 100 block code grid](https://lh3.googleusercontent.com/tXp9f3xuB_S83ek8RkoznTq1_sIqf2aH-zNH4aiBXxvzJwT58d5r2HStrCprvPhJMj6L9YkVaiSR2khAZeB4an2oDgveBt7zIcBypbxul1-xyhlx2QN4D0tnh3mYctoc64-GU93R6FXWgysyoixHkw6yq1dqAaIZT0bT8EKU_pZSKE-wIzniB_ofbzc-w6vssv4MiiWgcGcaq80jh8DdNK7fRQnOps5OFpOxuKJKJEZkyA1SzEKKPu-xTy9CwDq-AQuob3M0xGWNZEpJdoE4f2VhYqAcSUOCcX8X_TxggFFb-wvrk5F9cnowz2zAbikDHguvf3MfKuEj7xbIOrhQGXJI64NcywYPNv7AWWDnQFdVzjQEM9Z8-CKKBMXabvxN4lIamdjiYM8ssFpVF9KVEGQ0k7DZeNrEGHz1eeWprsDJOqxbDC8duDvqkFCwGuTqzGKxf3e8Ycevm6qFG3VPHEnSuwbF7UZbYe220jT5FrhzbP4ahnmPpvuy7mODhtM6RRpWJqqjq4GMScCV4n3hd_2h1HxHIAj_s4nRfrCnnGgKji8ZThxs959EOBHseMlenT3MDAnzzH31MO_DzPWCsxH7BYDRZJ46hAMlWExQHvNHBmSqpYWPnZcLEEdsIjs=w1186-h741-no) 18 | 19 | In freeform MineFriff, this code box is essentially non-existant; programs can take any form they want and can be even 3d. 20 | 21 | In textual MineFriff, the code box spans an infinite amount of space, both vertically and horizontally. 22 | 23 | ### Instruction Pointer (IP) 24 | The _instruction pointer_ is what drives the interpreting of MineFriff programs. It starts in the top left corner of the program (strict and textual) or wherever the player is standing in freeform. It can move left, right, up, down, and in the case of freeform MineFriff, up layers and down layers. In strict MineFriff, when the IP reaches the edge of the code box, the IP "wraps around" to the other side (e.g. if it reaches the far right side, it will go to the far left side and continue). This doesn't in happen in freeform MineFriff, as there isn't any code box to wrap around. This functionality is being worked on for textual MineFriff. 25 | 26 | ### The Temporary Register 27 | Rather than having literals pushed directly onto the stack, MineFriff has a _temporary register_ (temp reg) in which literals are constructed. This allows for any value to be created without having to worry about impacting the stack. The temp reg can be treated as either an `int`eger, a `char`acter or a `float`. 28 | 29 | ### The Stack 30 | The stack in MineFriff is like the stack in most other fungeoids -- it can have values pushed to it, it can have stack operations performed, it can be shifted left and right -- all of the usual stuff. However, when popping values from the stack, they are placed back into the temp reg (hence, overwriting the current value), rather than deleted into nowhere. 31 | 32 | ## Instructions 33 | | Command | Symbol | Minecraft Block Equivalent | 34 | |:------------------------------------------------------------------------------------------------------:|:------------------:|:---------------------------:| 35 | | Move the IP left. | `<` | Oak Wood Planks | 36 | | Move the IP right. | `>` | Spruce Wood Planks | 37 | | Move the IP up. | `^` | Birch Wood Planks | 38 | | Move the IP down. | `v` | Jungle Wood Planks | 39 | | Print the top value on the stack. | `o` | Sponge | 40 | | Add a literal to the temp reg. | `0123456789abcdef` | Wool (Data gives the value) | 41 | | Reverse the contents of the stack. | `r` | Netherrack | 42 | | Push the temp reg onto the stack and reset it to 0. | `,` | Piston | 43 | | Pop the top item from the stack and place it in the temp reg. | `.` | Sticky Piston | 44 | | Push the length of the stack onto the stack. | `l` | Stone | 45 | | Pop `x` and `y` off the stack. Push `y == x` back on (1 if true, 0 if false). | `=` | Bedrock | 46 | | Duplicate the top item of the stack. | `:` | Hopper | 47 | | Swap the top two items of the stack. | `$` | Obsidian | 48 | | Skip the next instruction (trampoline). | `#` | Slime Block | 49 | | Pop the top value from the stack and skip the next instruction if it is zero (conditional trampoline). | `?` | Redstone Lamp (off) | 50 | | End the program | `;` | Glass Block | 51 | | Pop `x` and `y` off the stack. Push `y > x` back on (1 if true, 0 if false). | `)` | Diamond Block | 52 | | Pop `x` and `y` off the stack. Push `y < x` back on (1 if true, 0 if false). | `(` | Coal Block | 53 | | Pop `x` and `y` off the stack. Push `y * x` back on. | `*` | Podzol | 54 | | Pop `x` and `y` off the stack. Push `y / x` back on. | `/` | Diorite (raw) | 55 | | Pop `x` and `y` off the stack. Push `y + x` back on. | `+` | Andesite (raw) | 56 | | Pop `x` and `y` off the stack. Push `y - x` back on. | `-` | Granite (raw) | 57 | | Change the direction of the IP to a random direction. | `x` | Dispenser | 58 | | Go back to the start of the current line in the same direction. | `~` | Hay Block | 59 | | Go to the start of the next line. | Backtick | End Bricks | 60 | | Treat the temp reg as an integer. | `I` | Packed Ice | 61 | | Treat the temp reg as a character. | `C` | Birch Wood | 62 | | Treat the temp reg as a float. | `F` | Furnace | 63 | | Right shift the stack by 1. | `]` | Dirt Block | 64 | | Left shift the stack by 1. | `[` | Cobblestone | 65 | | Pop `x` and `y` off the stack. Push `y % x` back on. | `%` | Sand (normal) | 66 | | Start/end a comment | `{...}` | N/A | 67 | | Delete the last item on the stack (pop it, without saving it) | `!` | N/A | 68 | 69 | 70 | ## Example Programs 71 | ### Hello World 72 | Cff3,a*a,9*c,a*b4,a*b1,8*a7,ff2,ffe,a*b1,9*c,:a*a1,9*8,` 73 | >l?#;o~ 74 | 75 | ![Hello world program in blocks](https://lh3.googleusercontent.com/0OLHfIK0dkMC0Wma5OhB6bTlxaDrmv1FlluEyqnh2wdCJjQK3fJ5ATUycUuixtIAODqi9Ix7P3bJ0FHE2bSJ0xln_1-fy-Fvz4pzGvxnthpH2XfQgKUdewKpCZGjF_JT0Gbz0yG0ypwYBfQRp08VQNb_JKbJRDGjz9Clpbgnk7WjdrsMnEHJAPeAjmoEtIc9k8qAOBm93xpldcUZxUTmBtpYDvvw0zsP0MKUdYLjoPE4DSDFe1RqqtDRt-H7ftrrUOoikdKVTZ7qnBzMFdjhIN9BdKsNJm9yDW7ZZlYhc9KvTaQWvWq_xZJ4ddr56kfwvkWWuAOGmbxsrC5ZfM0xDlMlkZiydVBLEtO8mJNi7QFb4fTkrMDKxbBqUWuCjKJMouHQp1LIPxaJ7WmRKJ693EfDBGfIuB6IHi6bO6lLx6K8JnykUR4hDSft56erMJnh5sObvy5s3-Gxn2QJ7FyDbk6wmCVqy-ZnL_ZwBsQ5hMdBxuU6Hlbzp8CfPVT6wF3cGFruHeDA3xd6DOvUsjzKEutpcKeNztBY-5YoYv3tomol8X0bEQ8fo21LGKLRUBf2iukj_IFpO0YXdkp6cb-jAI4c_-otIaHXz1Cyxyabe44Je7gppZxpiVTLBpUsWyk=w1264-h790-no) 76 | ### Fizzbuzz 77 | 0,` 78 | >I1,+:a*a1,=?;`~ o,aC < 79 | >:f,%?`Ca*c2,:a*b7,7*e,a*c2,:f*7,a*7,oooooooo^ 80 | >:5,%?`Ca*c2,:a*b7,6*b,oooo ^ 81 | >:3,%?`Ca*c2,:f*7,a*7,oooo ^ 82 | >:o ^ 83 | 84 | ![Fizzbuzz program in blocks](https://lh3.googleusercontent.com/9fY3GEMsNFk3bnzmMUUKA0wIxVUsah7l7vnHtszAcLgH7lI6nxrQ5EL9Z51NhViDt-_abKjW4rbZ5ayYw74tDQy2lLG8Ba9n56khZdZOBnkL55Lx7dxytT1C3WpR8yOR9wzIA3bg4OcclQDFdJ1HOQXM89qZeJb65HQpbn5ifWejxhinyCIFaO3xG_Ywnh4Ux0_U8xbSlSiU1ZsseN1xNfoDRnrCGEsKbeKZcd5Uvs37Mk9t7QhuL1ky-gIWdb5tRZZXJJ57-P0XaXH6kj64sgS8ev0cY6Uo3N9m1Dn_TZUhDOgL__DBCIsN5rEqCTXlrvXSEK6eQ7B-P6jFKp_Wc0uUqdw-tZAOpBpMkSDZiGtSXyQ289wwVnNVTsQumohPRwPm596R94-EMCigjN-bjP3nk_NjiRB6eRw8wkmw3OhgJVHlKgp_H0ssa0uV2Zy6R0y4CbeueEqNMK0DDiHXlNDb2_OIWW2lip0QtxKkWHn1ahLZq16-SkJo27oagPIh_lqubt-V3wLfKnl-C8NJrV4tt0ZxioxdxENCcf4uo03PWzIZiC5ezHH3VXbrApO8HdD6OAs31PAie7eyMUoK73mnYldz_WqooOHAYjVBbslfSIktq3GVJagOqtXAr8A=w1264-h790-no) 85 | 86 | ### 99 bottles of bear 87 | Ia*a,` 88 | >I1,-:0,=?;` ~ 89 | >Cff2,ffe,c*9,c*9,a*97,a*b9,ff2,a*a1,d*8,a*b6,ff2,a*b,a*b1,ff2,a*b4,a*a1,a*a1,e*7,ff2,a*a2,a*b1,ff2,a*b5,a*a1,c*9,a*b6,a*b6,a*b1,e*7,ff2,` 90 | >r:or` 91 | >lI1,=?`o~ 92 | >Ca,a*46,a*b4,a*a1,a*a1,e*7,ff2,a*a2,a*b1,ff2,a*b5,a*a1,c*9,a*b6,a*b6,a*b1,e*7,ff2,` 93 | >r:or` 94 | >lI1,=?`o~ 95 | >Cff2,ffe,a*a,a*b,d*9,a*b1,a*b4,a*97,ff2,a*b6,f*7,ff2,a*b5,a*b5,a*97,e*8,ff2,ffe,a*b,a*b9,a*b1,a*a,ff2,a*a1,a*b,a*b1,ff2,a*a1,a*a7,a*97,c*7,` 96 | >lI1,=?`o~ 97 | >Ca*46,c*9,c*9,a*97,a*b9,ff2,a*a1,d*8,a*b6,ff2,a*b,a*b1,ff2,a*b4,a*a1,a*a1,e*7,ff2,a*a2,a*b1,ff2,a*b5,a*a1,c*9,a*b6,a*b6,a*b1,e*7,ff2,` 98 | >r:I1,-or` 99 | >l1,=?`o~ 100 | >Ca,a,oo ^ 101 | -------------- 102 | # Installation Instructions for Strict and Freeform MineFriff 103 | You will need the [Raspberry Jam Mod](https://github.com/arpruss/raspberryjammod). The tutorial [here](https://www.instructables.com/id/Python-coding-for-Minecraft/) shows how to install it. Once you have the mod fully installed, drag `MineFriff.py` into the `mcpipy` folder. 104 | 105 | # To be Added 106 | - The ability to have input 107 | - More commands 108 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import random 2 | import ConstLib 3 | import sys 4 | 5 | stack = [] 6 | temp_reg = 0 7 | direction = ConstLib.RIGHT 8 | original_direction = ConstLib.RIGHT 9 | ip = (0, 0, 0) 10 | push_mode = int 11 | next_op = "+" 12 | in_peak = False 13 | peak_direction = ConstLib.PEAK_DIR.NONE 14 | spot = (0, 0, 0) 15 | in_comment = False 16 | 17 | 18 | 19 | def gridify(code): 20 | grid = [] 21 | temp_line = [] 22 | for line in code.split("\n"): 23 | temp_line += line 24 | grid.append(temp_line[::]) 25 | temp_line = [] 26 | 27 | return grid 28 | 29 | def next_cmd(ip): 30 | factor_x = 0 31 | factor_y = 0 32 | while is_empty(ip[0] + direction[0] + factor_x, 33 | ip[1] + direction[1] + factor_y): 34 | factor_x += direction[0] 35 | factor_y += direction[1] 36 | 37 | return (ip[0] + direction[0] + factor_x, 38 | ip[1] + direction[1] + factor_y, 39 | ip[2] + direction[2]) 40 | 41 | def is_empty(x, y): 42 | try: 43 | c = g[y][x] 44 | return False 45 | except IndexError: 46 | return True 47 | def run(grid): 48 | global stack, temp_reg, direction, ip, push_mode, next_op 49 | global peak_direction, original_direction, spot, in_comment 50 | 51 | cmd = grid[ip[1]][ip[0]] 52 | 53 | while cmd != ConstLib.END_PROG: 54 | if in_comment and cmd != ConstLib.COMMENT_END: 55 | ip = next_cmd(ip) 56 | #print(stack, cmd) 57 | cmd = grid[ip[1]][ip[0]] 58 | continue 59 | 60 | if cmd == ConstLib.COMMENT_END: 61 | in_comment = False 62 | 63 | elif cmd == ConstLib.COMMAND_LEFT: 64 | direction = ConstLib.LEFT 65 | 66 | elif cmd == ConstLib.COMMAND_RIGHT: 67 | direction = ConstLib.RIGHT 68 | 69 | elif cmd == ConstLib.COMMAND_DOWN: 70 | direction = ConstLib.DOWN 71 | 72 | elif cmd == ConstLib.COMMAND_UP: 73 | direction = ConstLib.UP 74 | 75 | elif cmd in ConstLib.LITERAL: 76 | if next_op == "*": 77 | temp_reg *= int(cmd, base=16) 78 | elif next_op == "+": 79 | temp_reg += int(cmd, base=16) 80 | elif next_op == "-": 81 | temp_reg -= int(cmd, base=16) 82 | elif next_op == "/": 83 | temp_reg /= int(cmd, base=16) 84 | elif next_op == "%": 85 | temp_reg %= int(cmd, base=16) 86 | next_op = "+" 87 | 88 | 89 | elif cmd == ConstLib.PUSH: 90 | stack.append(push_mode(temp_reg)) 91 | temp_reg = 0 92 | 93 | elif cmd == ConstLib.DELETE: 94 | del stack[-1] 95 | 96 | elif cmd == ConstLib.OUTPUT: 97 | print(stack[-1], end="") 98 | del stack[-1] 99 | 100 | elif cmd == ConstLib.INPUT: 101 | txt = input() 102 | for char in txt: 103 | stack.append(push_mode(char)) 104 | 105 | elif cmd == ConstLib.POP: 106 | temp_reg = stack[-1] 107 | del stack[-1] 108 | 109 | elif cmd == ConstLib.REVERSE: 110 | stack = stack[::-1] 111 | 112 | elif cmd == ConstLib.DUPLICATE: 113 | stack.append(stack[-1]) 114 | 115 | elif cmd == ConstLib.LENGTH: 116 | stack.append(len(stack)) 117 | 118 | elif cmd == ConstLib.RETURN: 119 | ip = (0, ip[1], ip[2]) 120 | cmd = grid[ip[1]][ip[0]] 121 | continue 122 | 123 | elif cmd == ConstLib.TRAMPOLINE: 124 | ip = next_cmd(ip) 125 | ip = next_cmd(ip) 126 | cmd = grid[ip[1]][ip[0]] 127 | continue 128 | 129 | elif cmd == ConstLib.CONDITIONAL: 130 | test = stack[-1] 131 | del stack[-1] 132 | 133 | if test == 0: 134 | ip = next_cmd(ip) 135 | ip = next_cmd(ip) 136 | cmd = grid[ip[1]][ip[0]] 137 | continue 138 | 139 | elif cmd == ConstLib.PUSH_CHR: 140 | push_mode = chr 141 | 142 | elif cmd == ConstLib.PUSH_INT: 143 | push_mode = int 144 | 145 | elif cmd == ConstLib.PUSH_FLOAT: 146 | push_mode = float 147 | 148 | elif cmd == ConstLib.MULTIPLY: 149 | if temp_reg == 0: 150 | x, y = stack[-1], stack[-2] 151 | if str not in [type(x), type(y)]: 152 | del stack[-1] 153 | del stack[-1] 154 | 155 | stack.append(x * y) 156 | else: 157 | raise Exception("Types for multiplication are different") 158 | else: 159 | next_op = "*" 160 | 161 | elif cmd == ConstLib.ADD: 162 | if temp_reg == 0: 163 | x, y = stack[-1], stack[-2] 164 | if str not in [type(x), type(y)]: 165 | del stack[-1] 166 | del stack[-1] 167 | 168 | stack.append(x + y) 169 | else: 170 | raise Exception("Types for addition are different") 171 | else: 172 | next_op = "+" 173 | 174 | 175 | elif cmd == ConstLib.SUBTRACT: 176 | if temp_reg == 0: 177 | x, y = stack[-1], stack[-2] 178 | if str not in [type(x), type(y)]: 179 | del stack[-1] 180 | del stack[-1] 181 | 182 | stack.append(y - x) 183 | else: 184 | raise Exception("Types for subtraction are different") 185 | else: 186 | next_op = "-" 187 | 188 | elif cmd == ConstLib.DIVIDE: 189 | if temp_reg == 0: 190 | x, y = stack[-1], stack[-2] 191 | if str not in [type(x), type(y)]: 192 | del stack[-1] 193 | del stack[-1] 194 | 195 | stack.append(y / x) 196 | else: 197 | raise Exception("Types for divison are different") 198 | else: 199 | next_op = "/" 200 | 201 | elif cmd == ConstLib.MODULUS: 202 | if temp_reg == 0: 203 | x, y = stack[-1], stack[-2] 204 | if str not in [type(x), type(y)]: 205 | del stack[-1] 206 | del stack[-1] 207 | 208 | stack.append(y % x) 209 | else: 210 | raise Exception("Types for modulus are different") 211 | else: 212 | next_op = "%" 213 | 214 | 215 | elif cmd == ConstLib.NEXT_LINE: 216 | ip = (0, ip[1] + 1, ip[2]) 217 | cmd = grid[ip[1]][ip[0]] 218 | continue 219 | 220 | elif cmd == ConstLib.EQUALS: 221 | if len(stack) >= 2: 222 | left, right = stack[-1], stack[-2] 223 | del stack[-1]; del stack[-1] 224 | if left == right: 225 | stack.append(1) 226 | else: 227 | stack.append(0) 228 | else: 229 | raise Exception("Not enough values for comparison for =") 230 | 231 | 232 | elif cmd == ConstLib.LESS_THAN: 233 | if len(stack) >= 2: 234 | left, right = stack[-1], stack[-2] 235 | del stack[-1]; del stack[-1] 236 | if left < right: 237 | stack.append(1) 238 | else: 239 | stack.append(0) 240 | else: 241 | raise Exception("Not enough values for comparison for <") 242 | 243 | elif cmd == ConstLib.GREATER_THAN: 244 | if len(stack) >= 2: 245 | left, right = stack[-1], stack[-2] 246 | del stack[-1]; del stack[-1] 247 | if left > right: 248 | stack.append(1) 249 | else: 250 | stack.append(0) 251 | else: 252 | raise Exception("Not enough values for comparison for >") 253 | 254 | 255 | elif cmd == ConstLib.L_SHIFT: 256 | val = stack[0] 257 | del stack[0] 258 | stack.append(val) 259 | 260 | elif cmd == ConstLib.R_SHIFT: 261 | val = stack[-1] 262 | del stack[-1] 263 | stack.insert(0, val) 264 | 265 | elif cmd == ConstLib.SWAP_TOP: 266 | stack[-1], stack[-2] = stack[-2], stack[-1] 267 | 268 | elif cmd == ConstLib.COMMENT_START: 269 | in_comment = True 270 | 271 | elif cmd == ConstLib.PEAK_UP: 272 | pass 273 | 274 | elif cmd == ConstLib.PRINT: 275 | #Not the single char output, but the smart function 276 | if len(stack) == 0 or type(stack[-1]) == str or stack[-1] == "*": 277 | print("".join(stack)) 278 | stack.clear() 279 | 280 | else: 281 | x = stack.pop() 282 | print("".join(stack[:x])) 283 | 284 | 285 | 286 | 287 | ip = next_cmd(ip) 288 | #print(stack, cmd) #uncomment to enable debug mode 289 | cmd = grid[ip[1]][ip[0]] 290 | 291 | if __name__ == '__main__': 292 | if len(sys.argv) > 1: 293 | import argparse 294 | parser = argparse.ArgumentParser() 295 | parser.add_argument("file", help="The location of the Minefriff file to open") 296 | 297 | args = parser.parse_args() 298 | file_location = args.file 299 | 300 | 301 | else: 302 | file_location = input("Enter the file location of the Minefriff program: ") 303 | 304 | code = open(file_location, encoding="utf-8").read().strip("\n") 305 | 306 | 307 | g = gridify(code) 308 | run(g) 309 | --------------------------------------------------------------------------------