Morpion solitaire: Difference between revisions
Content added Content deleted
(Added Wren) |
|||
Line 1,687: | Line 1,687: | ||
* number of wins = 47 |
* number of wins = 47 |
||
</pre> |
</pre> |
||
=={{header|Wren}}== |
|||
{{trans|C}} |
|||
{{libheader|ncurses}} |
|||
{{libheader|Wren-dynamic}} |
|||
{{libheader|Wren-fmt}} |
|||
An embedded program so we can use the ncurses library. |
|||
<lang ecmascript>/* morpion_solitaire.wren */ |
|||
import "random" for Random |
|||
import "./dynamic" for Flags, Struct |
|||
import "./fmt" for Conv |
|||
class Ncurses { |
|||
foreign static initscr() |
|||
foreign static cbreak() |
|||
foreign static nocbreak() |
|||
foreign static echo() |
|||
foreign static noecho() |
|||
foreign static refresh() |
|||
foreign static getch() |
|||
foreign static mvprintw(y, x, str) |
|||
foreign static timeout(delay) |
|||
foreign static endwin() |
|||
} |
|||
class C { |
|||
foreign static usleep(usec) |
|||
} |
|||
// optional settings |
|||
var lineLen = 5 |
|||
var disjoint = 0 |
|||
var fields = [ |
|||
"blank", "occupied", "dirNS", "dirEW", |
|||
"dirNESW", "dirNWSE", "newlyAdded", "current" |
|||
] |
|||
var State = Flags.create("State", fields, true) |
|||
var ofs = [ |
|||
[0, 1, State.dirNS], |
|||
[1, 0, State.dirEW], |
|||
[1, -1, State.dirNESW], |
|||
[1, 1, State.dirNWSE] |
|||
] |
|||
var Move = Struct.create("Move", ["m", "s", "seq", "x", "y"]) |
|||
var rand = Random.new() |
|||
var board |
|||
var width |
|||
var height |
|||
var allocBoard = Fn.new { |w, h| |
|||
var buf = List.filled(h, null) |
|||
for (i in 0...h) buf[i] = List.filled(w, 0) |
|||
return buf |
|||
} |
|||
var boardSet = Fn.new { |v, x0, y0, x1, y1| |
|||
for (i in y0..y1) { |
|||
for (j in x0..x1) board[i][j] = v |
|||
} |
|||
} |
|||
var initBoard = Fn.new { |
|||
width = height = 3 * (lineLen - 1) |
|||
board = allocBoard.call(width, height) |
|||
boardSet.call(State.occupied, lineLen-1, 1, 2*lineLen-3, height-2) |
|||
boardSet.call(State.occupied, 1, lineLen-1, width-2, 2*lineLen-3) |
|||
boardSet.call(State.blank, lineLen, 2, 2*lineLen-4, height-3) |
|||
boardSet.call(State.blank, 2, lineLen, width-3, 2*lineLen-4) |
|||
} |
|||
// -1: expand low index end; 1: expand high index end |
|||
var expandBoard = Fn.new { |dw, dh| |
|||
var dw2 = (dw == 0) ? 0 : 1 |
|||
var dh2 = (dh == 0) ? 0 : 1 |
|||
var nw = width + dw2 |
|||
var nh = height + dh2 |
|||
var nbuf = allocBoard.call(nw, nh) |
|||
dw = -Conv.btoi(dw < 0) |
|||
dh = -Conv.btoi(dh < 0) |
|||
for (i in 0...nh) { |
|||
if (i + dh < 0 || i + dh >= height) continue |
|||
for (j in 0...nw) { |
|||
if (j + dw < 0 || j + dw >= width) continue |
|||
nbuf[i][j] = board[i+dh][j+dw] |
|||
} |
|||
} |
|||
board = nbuf |
|||
width = nw |
|||
height = nh |
|||
} |
|||
var showBoard = Fn.new { |
|||
for (i in 0...height) { |
|||
for (j in 0...width){ |
|||
var temp |
|||
if (board[i][j] & State.current != 0) { |
|||
temp = "X " |
|||
} else if (board[i][j] & State.newlyAdded != 0) { |
|||
temp = "O " |
|||
} else if (board[i][j] & State.occupied != 0) { |
|||
temp = "+ " |
|||
} else { |
|||
temp = " " |
|||
} |
|||
Ncurses.mvprintw(i + 1, j * 2, temp) |
|||
} |
|||
} |
|||
Ncurses.refresh() |
|||
} |
|||
// test if a point can complete a line, or take that point |
|||
var testPosition = Fn.new { |y, x, rec| |
|||
if (board[y][x] & State.occupied != 0) return |
|||
for (m in 0..3) { // 4 directions |
|||
var dx = ofs[m][0] |
|||
var dy = ofs[m][1] |
|||
var dir = ofs[m][2] |
|||
var s = 1 - lineLen |
|||
while (s <= 0) { // offset line |
|||
var k = 0 |
|||
while (k < lineLen) { |
|||
if (s + k == 0) { |
|||
k = k + 1 |
|||
continue |
|||
} |
|||
var xx = x + dx * (s + k) |
|||
var yy = y + dy * (s + k) |
|||
if (xx < 0 || xx >= width || yy < 0 || yy >= height) break |
|||
// no piece at position |
|||
if (board[yy][xx] & State.occupied == 0) break |
|||
// this direction taken |
|||
if (board[yy][xx] & dir != 0) break |
|||
k = k + 1 |
|||
} |
|||
if (k == lineLen) { |
|||
// position ok |
|||
// random integer to even each option's chance of being picked |
|||
rec.seq = rec.seq + 1 |
|||
if (rand.int(rec.seq) == 0) { |
|||
rec.m = m |
|||
rec.s = s |
|||
rec.x = x |
|||
rec.y = y |
|||
} |
|||
} |
|||
s = s + 1 |
|||
} |
|||
} |
|||
} |
|||
var addPiece = Fn.new { |rec| |
|||
var dx = ofs[rec.m][0] |
|||
var dy = ofs[rec.m][1] |
|||
var dir = ofs[rec.m][2] |
|||
board[rec.y][rec.x] = board[rec.y][rec.x] | (State.current | State.occupied) |
|||
for (k in 0...lineLen) { |
|||
var xx = rec.x + dx * (k + rec.s) |
|||
var yy = rec.y + dy * (k + rec.s) |
|||
board[yy][xx] = board[yy][xx] | State.newlyAdded |
|||
if (k >= disjoint || k < lineLen-disjoint) { |
|||
board[yy][xx] = board[yy][xx] | dir |
|||
} |
|||
} |
|||
} |
|||
var nextMove = Fn.new { |
|||
var rec = Move.new(0, 0, 0, 0, 0) |
|||
// wipe last iteration's new line markers |
|||
for (i in 0...height) { |
|||
for (j in 0...width) { |
|||
board[i][j] = board[i][j] & ~(State.newlyAdded | State.current) |
|||
} |
|||
} |
|||
// randomly pick one of next legal moves |
|||
for (i in 0...height) { |
|||
for (j in 0...width) testPosition.call(i, j, rec) |
|||
} |
|||
// didn't find any move, game over |
|||
if (rec.seq == 0) return false |
|||
addPiece.call(rec) |
|||
if (rec.x == width-1) { |
|||
rec.x = 1 |
|||
} else if (rec.x != 0) { |
|||
rec.x = 0 |
|||
} else { |
|||
rec.x = -1 |
|||
} |
|||
if (rec.y == height-1) { |
|||
rec.y = 1 |
|||
} else if (rec.y != 0) { |
|||
rec.y = 0 |
|||
} else { |
|||
rec.y = -1 |
|||
} |
|||
if (rec.x != 0 || rec.y != 0) expandBoard.call(rec.x, rec.y) |
|||
return true |
|||
} |
|||
initBoard.call() |
|||
Ncurses.initscr() |
|||
Ncurses.noecho() |
|||
Ncurses.cbreak() |
|||
var ch = 0 |
|||
var move = 0 |
|||
var waitKey = true |
|||
while (true) { |
|||
Ncurses.mvprintw(0, 0, "Move %(move)") |
|||
move = move + 1 |
|||
showBoard.call() |
|||
if (!nextMove.call()) { |
|||
nextMove.call() |
|||
showBoard.call() |
|||
break |
|||
} |
|||
if (!waitKey) C.usleep(100000) |
|||
if ((ch = Ncurses.getch()) == 32) { // spacebar pressed |
|||
waitKey = !waitKey |
|||
if (waitKey) { |
|||
Ncurses.timeout(-1) |
|||
} else { |
|||
Ncurses.timeout(0) |
|||
} |
|||
} |
|||
if (ch == 113) break // 'q' pressed |
|||
} |
|||
Ncurses.timeout(-1) |
|||
Ncurses.nocbreak() |
|||
Ncurses.echo() |
|||
Ncurses.endwin()</lang> |
|||
<br> |
|||
We now embed the above script in the following C program, build and run it. |
|||
<lang c>/* gcc morpion_solitaire.c -o morpion_solitaire -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(); |
|||
} |
|||
void C_cbreak(WrenVM* vm) { |
|||
cbreak(); |
|||
} |
|||
void C_nocbreak(WrenVM* vm) { |
|||
nocbreak(); |
|||
} |
|||
void C_echo(WrenVM* vm) { |
|||
echo(); |
|||
} |
|||
void C_noecho(WrenVM* vm) { |
|||
noecho(); |
|||
} |
|||
void C_refresh(WrenVM* vm) { |
|||
refresh(); |
|||
} |
|||
void C_getch(WrenVM* vm) { |
|||
int ch = getch(); |
|||
wrenSetSlotDouble(vm, 0, (double)ch); |
|||
} |
|||
void C_mvprintw(WrenVM* vm) { |
|||
int y = (int)wrenGetSlotDouble(vm, 1); |
|||
int x = (int)wrenGetSlotDouble(vm, 2); |
|||
const char *str = wrenGetSlotString(vm, 3); |
|||
mvprintw(y, x, str); |
|||
} |
|||
void C_timeout(WrenVM* vm) { |
|||
int delay = (int)wrenGetSlotDouble(vm, 1); |
|||
timeout(delay); |
|||
} |
|||
void C_endwin(WrenVM* vm) { |
|||
endwin(); |
|||
} |
|||
void C_usleep(WrenVM* vm) { |
|||
useconds_t usec = (useconds_t)wrenGetSlotDouble(vm, 1); |
|||
usleep(usec); |
|||
} |
|||
WrenForeignMethodFn bindForeignMethod( |
|||
WrenVM* vm, |
|||
const char* module, |
|||
const char* className, |
|||
bool isStatic, |
|||
const char* signature) { |
|||
if (strcmp(module, "main") == 0) { |
|||
if (strcmp(className, "Ncurses") == 0) { |
|||
if (isStatic && strcmp(signature, "initscr()") == 0) return C_initscr; |
|||
if (isStatic && strcmp(signature, "cbreak()") == 0) return C_cbreak; |
|||
if (isStatic && strcmp(signature, "noecho()") == 0) return C_noecho; |
|||
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, "refresh()") == 0) return C_refresh; |
|||
if (isStatic && strcmp(signature, "getch()") == 0) return C_getch; |
|||
if (isStatic && strcmp(signature, "mvprintw(_,_,_)") == 0) return C_mvprintw; |
|||
if (isStatic && strcmp(signature, "timeout(_)") == 0) return C_timeout; |
|||
if (isStatic && strcmp(signature, "endwin()") == 0) return C_endwin; |
|||
} else if (strcmp(className, "C") == 0) { |
|||
if (isStatic && strcmp(signature, "usleep(_)") == 0) return C_usleep; |
|||
} |
|||
} |
|||
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 = "morpion_solitaire.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; |
|||
}</lang> |