Greed/Wren

From Rosetta Code

Code

Translation of: Go
Library: ncurses
Library: Wren-dynamic
Library: Wren-str
Library: Wren-fmt
Works with: Ubuntu 20.04

An embedded script so we can use the ncurses library.

/* Greed.wren */

import "./dynamic" for Struct
import "./str" for Char
import "./fmt" for Fmt
import "random" for Random

// all methods assume stdscr
class NC {
    foreign static initScr()
    foreign static useDefaultColors()
    foreign static startColor()

    foreign static cbreak()
    foreign static nocbreak()

    foreign static echo()
    foreign static noecho()

    foreign static cursSet(visibility)

    foreign static initPair(index, fg, bg)
    foreign static attron(cp)
    foreign static attroff(cp)

    foreign static move(x, y)
    foreign static mvaddStr(x, y, str)

    foreign static getch()

    foreign static refresh()

    foreign static endwin()
}

var COLOR_DEFAULT = -1
var COLOR_BLACK   = 0
var COLOR_RED     = 1
var COLOR_GREEN   = 2
var COLOR_YELLOW  = 3
var COLOR_BLUE    = 4
var COLOR_MAGENTA = 5
var COLOR_CYAN    = 6
var COLOR_WHITE   = 7

var A_BOLD = 0x200000

var Coord = Struct.create("Coord", ["x", "y"])

var width  = 79
var height = 22
var nCount = width * height

var board = List.filled(nCount, 0)
var score = 0
var cursor = Coord.new(0, 0)

var rand = Random.new()

var colors = [
    COLOR_DEFAULT,
    COLOR_WHITE,
    COLOR_BLACK   | A_BOLD,
    COLOR_BLUE    | A_BOLD,
    COLOR_GREEN   | A_BOLD,
    COLOR_CYAN    | A_BOLD,
    COLOR_RED     | A_BOLD,
    COLOR_MAGENTA | A_BOLD,
    COLOR_YELLOW  | A_BOLD,
    COLOR_WHITE   | A_BOLD
]

var printAt = Fn.new { |x, y, s, cp|
    NC.attron(cp)
    NC.mvaddStr(y, x, s)
    NC.attroff(cp)
}

var printScore = Fn.new {
    NC.move(24, 0)
    var s = Fmt.swrite("      SCORE: $d : $0.3f\%      ", score, score * 100 / nCount)
    printAt.call(0, 24, s, colors[4] + 10)
    NC.refresh()
    NC.attroff(colors[4] + 10)
}

var createBoard = Fn.new {
    for (y in 0...height) {
        for (x in 0...width) board[x + width*y] = rand.int(9) + 1
    }
    cursor = Coord.new(rand.int(width), rand.int(height))
    board[cursor.x + width*cursor.y] = 0
    score = 0
    printScore.call()
}

var displayBoard = Fn.new() {
    NC.move(0, 0)
    for (y in 0...height) {
        for (x in 0...width) {
            var i = board[x + width*y]
            var s = " "
            if (i > 0) s = i.toString
            printAt.call(x, y, s, i + 1)
        }
    }
    NC.move(cursor.y, cursor.x)
    printAt.call(cursor.x, cursor.y, "@", 10)
    NC.refresh()
    NC.attroff(10)
    NC.move(cursor.y, cursor.x)
    NC.cursSet(1)
}

var countSteps = Fn.new { |i, x, y|
    var t = Coord.new(cursor.x, cursor.y)
    while (i != 0) {
        i = i - 1
        t.x = t.x + x
        t.y = t.y + y
        if (t.x < 0 || t.y < 0 || t.x >= width || t.y >= height || board[t.x + width*t.y] == 0) {
            return false
        }
    }
    return true
}

var execute = Fn.new { |x, y|
    var i = board[cursor.x + x + width*(cursor.y + y)]
    if (countSteps.call(i, x, y)) {
        score = score + i
        while (i != 0) {
            i = i - 1
            cursor.x = cursor.x + x
            cursor.y = cursor.y + y
            board[cursor.x + width*cursor.y] = 0
        }
    }
}

var existsMoves = Fn.new {
    for (y in -1..1) {
        for (x in -1..1) {
            if (x == 0 && y == 0) continue
            var ix = cursor.x + x + width*(cursor.y + y)
            var i = 0
            if (ix >= 0 && ix < board.count) i = board[ix]
            if (i > 0 && countSteps.call(i, x, y)) return true
        }
    }
    return false
}
                     
var play = Fn.new {
    NC.initScr()
    NC.useDefaultColors()
    NC.startColor()
    for (i in 0...colors.count) {
        NC.initPair(i+1,  colors[i], -1)
        NC.initPair(i+11, colors[i], 2)
    }
    NC.cbreak()
    NC.noecho()
    while (true) {
        NC.cursSet(0)
        createBoard.call()
        while (true) {
            displayBoard.call()
            var key = Char.upper(Char.fromCode(NC.getch()))
            if (key == "Q") {
                if (cursor.x > 0 && cursor.y > 0) execute.call(-1, -1)
            } else if (key == "W") {
                if (cursor.y > 0) execute.call(0, -1)
            } else if (key == "E") {
                if (cursor.x < width - 1 && cursor.y > 0) execute.call(1, -1)
            } else if (key == "A") {
                if (cursor.x > 0) execute.call(-1, 0)
            } else if (key == "D") {
                if (cursor.x < width - 1) execute.call(1, 0)
            } else if (key == "Z") {
                if (cursor.x > 0 && cursor.y < height - 1) execute.call (-1, 1)
            } else if(key == "X") {
                if (cursor.y < height - 1) execute.call(0, 1)
            } else if (key == "C") {
                if (cursor.x < width - 1 && cursor.y < height - 1) execute.call(1, 1)
            } else if (key == "L") {  // leave key
               return
            }
            printScore.call()
            if (!existsMoves.call()) break
        }
        displayBoard.call()
        var cp = 10
        printAt.call(19,  8, "+----------------------------------------+", cp)
        printAt.call(19,  9, "|               GAME OVER                |", cp)
        printAt.call(19, 10, "|            PLAY AGAIN(Y/N)?            |", cp)
        printAt.call(19, 11, "+----------------------------------------+", cp)
        NC.move(10, 48)
        NC.cursSet(1)
        NC.refresh()
        var yn = Char.upper(Char.fromCode(NC.getch()))
        if (yn != "Y") return
    }
}

play.call()
NC.nocbreak()
NC.echo()
NC.endwin()
NC.cursSet(1)


We now embed this in the following C program, build and run it.

/* gcc Greed.c -o Greed -lncurses -lwren -lm */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include <unistd.h>
#include "wren.h"

/* C <=> Wren interface functions */

void C_initScr(WrenVM* vm) {
    initscr();
    use_default_colors();
}

void C_useDefaultColors(WrenVM* vm) {
    use_default_colors();
}

void C_startColor(WrenVM* vm) {    
    int res = start_color();
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_cbreak(WrenVM* vm) {
    int res = cbreak();
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_nocbreak(WrenVM* vm) {
    int res = nocbreak();
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_keypad(WrenVM* vm) {
    bool bf = wrenGetSlotBool(vm, 1);
    int res = keypad(stdscr, bf);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_echo(WrenVM* vm) {
    int res = echo();
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_noecho(WrenVM* vm) {
    int res = noecho();
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_cursSet(WrenVM* vm) {
    int visibility = (int)wrenGetSlotDouble(vm, 1);
    int res = curs_set(visibility);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_initPair(WrenVM* vm) {
    short index = (short)wrenGetSlotDouble(vm, 1);
    short fg = (short)wrenGetSlotDouble(vm, 2);  
    short bg = (short)wrenGetSlotDouble(vm, 3);
    int res = init_pair(index, fg, bg);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_attron(WrenVM* vm) {
    int index = (int)wrenGetSlotDouble(vm, 1);    
    int res = attron(COLOR_PAIR(index));
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_attroff(WrenVM* vm) {
    int index = (int)wrenGetSlotDouble(vm, 1);   
    int res = attroff(COLOR_PAIR(index));
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_move(WrenVM* vm) {
    int y = (int)wrenGetSlotDouble(vm, 1);
    int x = (int)wrenGetSlotDouble(vm, 2);
    int res = move(y, x);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_mvaddStr(WrenVM* vm) {
    int y = (int)wrenGetSlotDouble(vm, 1);
    int x = (int)wrenGetSlotDouble(vm, 2);
    const char *str = wrenGetSlotString(vm, 3);
    int res = mvaddstr(y, x, str);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_getch(WrenVM* vm) {
    int ch = getch();
    wrenSetSlotDouble(vm, 0, (double)ch);
}

void C_refresh(WrenVM* vm) {
    int res = refresh();
    wrenSetSlotDouble(vm, 0, (double)res);
}

void C_endwin(WrenVM* vm) {
    int res = endwin();
    wrenSetSlotDouble(vm, 0, (double)res);
}

WrenForeignMethodFn bindForeignMethod(
    WrenVM* vm,
    const char* module,
    const char* className,
    bool isStatic,
    const char* signature) {
    if (strcmp(module, "main") == 0) {
        if (strcmp(className, "NC") == 0) {
            if (isStatic && strcmp(signature, "initScr()") == 0)          return C_initScr;
            if (isStatic && strcmp(signature, "useDefaultColors()") == 0) return C_useDefaultColors;
            if (isStatic && strcmp(signature, "startColor()") == 0)       return C_startColor;
            if (isStatic && strcmp(signature, "cbreak()") == 0)           return C_cbreak;
            if (isStatic && strcmp(signature, "nocbreak()") == 0)         return C_nocbreak;
            if (isStatic && strcmp(signature, "echo()") == 0)             return C_echo;
            if (isStatic && strcmp(signature, "noecho()") == 0)           return C_noecho;
            if (isStatic && strcmp(signature, "cursSet(_)") == 0)         return C_cursSet;
            if (isStatic && strcmp(signature, "initPair(_,_,_)") == 0)    return C_initPair;
            if (isStatic && strcmp(signature, "attron(_)") == 0)          return C_attron;
            if (isStatic && strcmp(signature, "attroff(_)") == 0)         return C_attroff;
            if (isStatic && strcmp(signature, "move(_,_)") == 0)          return C_move;
            if (isStatic && strcmp(signature, "mvaddStr(_,_,_)") == 0)    return C_mvaddStr;
            if (isStatic && strcmp(signature, "getch()") == 0)            return C_getch;
            if (isStatic && strcmp(signature, "refresh()") == 0)          return C_refresh;
            if (isStatic && strcmp(signature, "endwin()") == 0)           return C_endwin;
        }
    }
    return NULL;
}

static void writeFn(WrenVM* vm, const char* text) {
    printf("%s", text);
}

void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {
    switch (errorType) {
        case WREN_ERROR_COMPILE:
            printf("[%s line %d] [Error] %s\n", module, line, msg);
            break;
        case WREN_ERROR_STACK_TRACE:
            printf("[%s line %d] in %s\n", module, line, msg);
            break;
        case WREN_ERROR_RUNTIME:
            printf("[Runtime Error] %s\n", msg);
            break;
    }
}

char *readFile(const char *fileName) {
    FILE *f = fopen(fileName, "r");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    rewind(f);
    char *script = malloc(fsize + 1);
    fread(script, 1, fsize, f);
    fclose(f);
    script[fsize] = 0;
    return script;
}

static void loadModuleComplete(WrenVM* vm, const char* module, WrenLoadModuleResult result) {
    if( result.source) free((void*)result.source);
}

WrenLoadModuleResult loadModule(WrenVM* vm, const char* name) {
    WrenLoadModuleResult result = {0};
    if (strcmp(name, "random") != 0 && strcmp(name, "meta") != 0) {
        result.onComplete = loadModuleComplete;
        char fullName[strlen(name) + 6];
        strcpy(fullName, name);
        strcat(fullName, ".wren");
        result.source = readFile(fullName);
    }
    return result;
}

int main(int argc, char **argv) {
    WrenConfiguration config;
    wrenInitConfiguration(&config);
    config.writeFn = &writeFn;
    config.errorFn = &errorFn;
    config.bindForeignMethodFn = &bindForeignMethod;
    config.loadModuleFn = &loadModule;
    WrenVM* vm = wrenNewVM(&config);
    const char* module = "main";
    const char* fileName = "Greed.wren";
    char *script = readFile(fileName);
    WrenInterpretResult result = wrenInterpret(vm, module, script);
    switch (result) {
        case WREN_RESULT_COMPILE_ERROR:
            printf("Compile Error!\n");
            break;
        case WREN_RESULT_RUNTIME_ERROR:
            printf("Runtime Error!\n");
            usleep(10000000); // allow time to read it
            timeout(-1);
            nocbreak();
            echo();
            endwin();
            break;
        case WREN_RESULT_SUCCESS:
            break;
    }
    wrenFreeVM(vm);
    free(script);
    return 0;
}