Morpion solitaire: Difference between revisions

Added Wren
(Added Wren)
Line 1,687:
* number of wins = 47
</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>
9,482

edits