Morpion solitaire: Difference between revisions

→‎{{header|Wren}}: Minor tidy and fixed problem with 'mvprintw' now requiring a format specifier.
(→‎{{header|REXX}}: reduce the program size, elided code to support boxing of the grid, added/changed comments, simplified code.)
(→‎{{header|Wren}}: Minor tidy and fixed problem with 'mvprintw' now requiring a format specifier.)
 
(8 intermediate revisions by 6 users not shown)
Line 12:
There are several variations of the game, this task deals with the 5 point "touching" version also known as "5T".
 
MorpianMorpion solitaire is played on a (theoretically) infinite grid. It begins with 36 points marked in a Greek cross:
<pre>
...XXXX...
Line 56:
=={{header|C}}==
Console play with ncurses. Length and touching rules can be changed at the begining of source code. 'q' key to quit, space key to toggle auto move, anykey for next move. Play is random. I got nowhere near the record 177 moves, but did approach the worst-possible (20) quite often.
<langsyntaxhighlight Clang="c">#include <ncurses.h>
#include <stdlib.h>
#include <unistd.h>
Line 280:
endwin();
return 0;
}</langsyntaxhighlight>
 
=={{header|Go}}==
{{libheader|goncurses}}
{{trans|C}}
<langsyntaxhighlight lang="go">package main
 
import (
Line 552:
gc.CBreak(false)
gc.Echo(true)
}</langsyntaxhighlight>
 
=={{header|Icon}} and {{header|Unicon}}==
Line 563:
=={{header|J}}==
With this program as the file m.ijs
<syntaxhighlight lang="j">
<lang J>
NB. turn will be a verb with GRID as y, returning GRID. Therefor:
NB. morpion is move to the power of infinity---convergence.
Line 671:
NB. example
smoutput play''
</syntaxhighlight>
</lang>
load the file into a j session to play an initial game and report the number of turns. We can play a game providing a vector of move numbers at which to report the output.
<pre>
Line 804:
See:[[Morpion solitaire/Julia]]
 
=={{header|Nim}}==
{{trans|Go}}
{{libheader|nim-ncurses}}
 
<syntaxhighlight lang="nim">import os, random, sequtils
import ncurses
 
const
LineLength = 5
Disjoint = 0
 
type
State {.pure.} = enum Blank, Occupied, DirNS, DirEW, DirNESW, DirNWSE, NewlyAdded, Current
States = set[State]
Board = seq[seq[States]]
Move = tuple[m, s, seqnum, x, y: int]
 
const Ofs = [(0, 1, DirNS), (1, 0, DirEW), (1, -1, DirNESW), (1, 1, DirNWSE)]
 
 
func set(board: var Board; value: State; x0, y0, x1, y1: int) =
for i in y0..y1:
for j in x0..x1:
board[i][j] = {value}
 
 
func initBoard(): Board =
let height, width = 3 * (LineLength - 1)
result = newSeqWith(height, newSeq[States](width))
result.set(Occupied, LineLength - 1, 1, 2 * LineLength - 3, height - 2)
result.set(Occupied, 1, LineLength - 1, width - 2, 2 * LineLength - 3)
result.set(Blank, LineLength, 2, 2 * LineLength - 4, height - 3)
result.set(Blank, 2, LineLength, width - 3, 2 * LineLength - 4)
 
 
func expand(board: var Board; dw, dh: int) =
 
# -1: expand low index end, +1: expand high index end.
let
height = board.len
width = board[0].len
nw = width + ord(dw != 0)
nh = height + ord(dh != 0)
 
var nboard = newSeqWith(nh, newSeq[States](nw))
let dw = -ord(dw < 0)
let dh = -ord(dh < 0)
 
for i in 0..<nh:
if i + dh notin 0..<height: continue
for j in 0..<nw:
if j + dw notin 0..<width: continue
nboard[i][j] = board[i + dh][j + dw]
 
board = move(nboard)
 
 
proc show(board: Board) =
for i, row in board:
for j, cell in row:
let str = if Current in cell: "X "
elif NewlyAdded in cell: "0 "
elif Occupied in cell: "+ "
else: " "
mvprintw(cint(i + 1), cint(j + 2), str)
refresh()
 
 
proc testPosition(board: Board; y, x: int; rec: var Move) =
let height = board.len
let width = board[0].len
if Occupied in board[y][x]: return
 
for m, (dx, dy, dir) in Ofs: # 4 directions.
for s in (1 - LineLength)..0: # offset line.
var k = -1
while k < LineLength:
inc k
if s + k == 0: continue
let xx = x + dx * (s + k)
let yy = y + dy * (s + k)
if xx < 0 or xx >= width or yy < 0 or yy >= height: break
if Occupied notin board[yy][xx]: break # No piece at position.
if dir in board[yy][xx]: break # This direction taken.
if k != LineLength: continue
 
# Position ok.
# Rand to even each option chance of being picked.
if rand(rec.seqnum) == 0:
rec.m = m; rec.s = s; rec.x = x; rec.y = y
inc rec.seqnum
 
 
proc addPiece(board: var Board; rec: Move) =
let (dx, dy, dir) = Ofs[rec.m]
board[rec.y][rec.x] = board[rec.y][rec.x] + {Current, Occupied}
for k in 0..<LineLength:
let xx = rec.x + dx * (k + rec.s)
let yy = rec.y + dy * (k + rec.s)
board[yy][xx].incl NewlyAdded
if k >= Disjoint or k < LineLength - Disjoint:
board[yy][xx].incl dir
 
 
proc nextMove(board: var Board): bool {.discardable.} =
var rec: Move
let maxi = board.high
let maxj = board[0].high
 
# Wipe last iteration new line markers.
for row in board.mitems:
for cell in row.mitems:
cell = cell - {NewlyAdded, Current}
 
# Randomly pick one of next legal move.
for i in 0..maxi:
for j in 0..maxj:
board.testPosition(i, j, rec)
 
# Didn't find any move, game over.
if rec.seqnum == 0: return false
 
board.addPiece(rec)
 
rec.x = if rec.x == maxj: 1
elif rec.x != 0: 0
else: -1
rec.y = if rec.y == maxi: 1
elif rec.y != 0: 0
else: -1
 
if rec.x != 0 or rec.y != 0: board.expand(rec.x, rec.y)
result = true
 
 
proc play() =
randomize()
var board = initBoard()
var waitKey = true
let win {.used.} = initscr()
noecho()
cbreak()
 
var move = 0
while true:
mvprintw(0, 0, "Move %d", move)
inc move
board.show()
if not board.nextMove():
board.nextMove()
board.show()
break
if not waitKey: sleep(100)
let ch = getch()
if ch == ord(' '):
waitKey = not waitKey
if waitKey: timeout(-1)
else: timeout(0)
elif ch == ord('q'):
break
 
timeout(-1)
getch()
nocbreak()
onecho()
endwin()
 
play()</syntaxhighlight>
 
{{out}}
Intermediate state:
<pre>Move 20
 
+
+++++
+ ++
+ ++ ++ +
+++++ +0+++
+ + 0 +
+ + X +
++++0+++++
+0 ++
++ +
+++++
+
</pre>
 
=={{header|Perl}}==
Picks a move at random from all possible moves at each step. A sample game is shown.
The largest score found so far (from just random play) is 92, also shown below.
<syntaxhighlight lang="perl">use strict;
use warnings;
use List::Util qw( none );
 
local $_ = <<END;
.............XXXX.............
.............X..X.............
.............X..X.............
..........XXXX..XXXX..........
..........X........X..........
..........X........X..........
..........XXXX..XXXX..........
.............X..X.............
.............X..X.............
.............XXXX.............
END
$_ = tr/X/ /r . $_ . tr/X/ /r; # expand to 30x30
tr/./ /; # and convert dots to spaces
 
my @moves;
my %used;
my $count = 0;
while( 1 )
{
# print s/\A(?: +\n)*|(?:^ +\n)*\z//gmr, "count $count\n"; # uncomment for each step
tr/O/X/;
my @try; # find valid moves
for my $i ( 0, 29 .. 31 )
{
my $gap = qr/.{$i}/s;
while( / (?=$gap(X)$gap(X)$gap(X)$gap(X))/g ) # add to top
{
my $cand = join ' ', map $-[$_], 0 .. 4;
none { $used{$_} } $cand =~ /(?=\b(\d+ \d+)\b)/g and push @try, $cand;
}
while( /X(?=$gap(.)$gap(.)$gap(.)$gap(.))/g ) # add inside/bottom downward
{
"$1$2$3$4" =~ tr/X// == 3 or next;
my $cand = join ' ', map $-[$_], 0 .. 4;
none { $used{$_} } $cand =~ /(?=\b(\d+ \d+)\b)/g and push @try, $cand;
}
}
@try ? $count++ : last;
my $pick = $try[rand @try]; #pick one valid move
push @moves, $pick;
for my $pos (split ' ', $pick)
{
substr $_, $pos, 1, 'O';
}
$used{$1} = 1 while $pick =~ /(?=\b(\d+ \d+)\b)/g;
}
print join(' ', map s/ .* /->/r =~ s!\d+! ($& % 31).','.int $& / 31 !ger, @moves)
=~ s/.{60}\K /\n/gr, "\n";
tr/O/X/;
print $_, "move count: $count\n";</syntaxhighlight>
This runs on a 30x30 grid (as advised by Talk page).
Each move is shown as the end points of a line, startcolumn,startrow->endcolumn,endrow where row and column numbers
are zero-based. To replay a game, just add all five points from each line to the grid. The final grid is also shown in full.
Uncommenting the early print will show each step with the latest line added as the 'O' character.
{{out}}
<pre>
10,15->14,19 13,16->13,20 10,13->10,17 15,16->19,16 15,10->19,14
12,10->16,10 19,12->19,16 16,16->16,20 9,16->13,16 12,16->16,20
16,13->20,13 10,13->14,13 13,9->13,13 16,9->16,13 16,9->12,13
13,19->17,19 14,10->10,14 19,15->15,19 20,13->16,17 13,9->17,13
13,11->17,11
X X
XXXXX
XXXXX
XX XX X
XXXXX XXXXX
X X
X XX
XXXXX XXXXX
X XX XX
XX X
XXXXX
X X
move count: 21
</pre>
<pre>
13,16->13,20 16,15->16,19 15,10->19,14 16,13->20,13 10,16->14,16
13,10->17,10 12,19->16,19 10,15->14,19 14,10->10,14 10,13->14,13
15,16->19,16 19,13->19,17 16,15->12,19 17,16->13,20 10,13->10,17
13,16->17,20 10,17->14,17 19,15->15,19 16,11->16,15 17,13->13,17
13,9->13,13 13,9->17,13 15,15->15,19 14,17->18,17 14,13->18,17
13,13->17,17 14,13->14,17 15,14->11,18 9,16->13,20 13,12->9,16
11,14->11,18 12,13->16,17 12,14->16,14 14,13->10,17 13,13->9,17
9,12->13,16 11,15->15,19 10,15->14,15 12,14->12,18 13,18->17,18
10,12->14,16 9,15->13,19 15,13->11,17 9,12->13,12 15,11->15,15
20,15->16,19 17,16->17,20 15,13->19,17 14,15->18,15 16,16->12,20
17,12->17,16 13,12->17,12 18,9->14,13 13,10->17,14 12,11->16,11
17,9->13,13 14,16->18,20 18,13->18,17 14,9->10,13 16,14->20,14
21,12->17,16 12,10->12,14 11,9->15,13 14,8->14,12 14,8->10,12
11,10->11,14 10,9->14,13 10,9->14,9 20,12->16,16 20,11->20,15
20,11->16,15 17,12->21,12 16,10->20,14 17,8->17,12 17,8->21,12
19,10->15,14 17,8->13,12 18,9->18,13 19,9->15,13 19,9->19,13
15,9->19,9 17,11->21,11 15,8->19,12 16,8->20,12 13,8->17,8 13,8->9,12 l
15,7->15,11 10,9->10,13 16,7->16,11 9,10->13,10 9,9->13,13 8,9->12,13
XX
XXXXX
XXXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXXX
XXXXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXX
XXXXXX
XX XX
move count: 92
</pre>
 
A faster, shorter version without the single step display.
Uses the same kind of block shift/or technology I used in "Forest fire" and have used
for Conway's Life.
<syntaxhighlight lang="perl">use strict;
use warnings;
use feature 'bitwise';
use List::Util 'none';
 
local $_ = <<END;
.............XXXX.............
.............X..X.............
.............X..X.............
..........XXXX..XXXX..........
..........X........X..........
..........X........X..........
..........XXXX..XXXX..........
.............X..X.............
.............X..X.............
.............XXXX.............
END
$_ = tr/X./ /r . tr/./ /r . tr/X./ /r; # expand to 30x30 and spaces
 
my($count, @moves, %used) = 0;
while( 1 )
{
my @try; # valid moves
for my $i ( 1, 30 .. 32 ) # directions 1 - 30 / 31 | 32 \
{
my $combined = tr/X \n/A\0/r |.
(substr $_, $i) =~ tr/X \n/B\0/r |.
(substr $_, 2 * $i) =~ tr/X \n/D\0/r |.
(substr $_, 3 * $i) =~ tr/X \n/H\0/r |.
(substr $_, 4 * $i) =~ tr/X \n/P\0/r;
while( $combined =~ /[OW\[\]\^]/g ) # exactly four Xs and one space
{
my $cand = join ' ', map $-[0] + $_ * $i, 0 .. 4;
none { $used{$_} } $cand =~ /(?=\b(\d+ \d+)\b)/g and push @try, $cand;
}
}
@try ? $count++ : last;
my $pick = $try[rand @try]; #pick one valid move
push @moves, $pick;
for my $pos (split ' ', $pick)
{
substr $_, $pos, 1, 'X';
}
@used{ $pick =~ /(?=\b(\d+ \d+)\b)/g } = (1) x 4;
}
print join(' ', map s/ .* /->/r =~ s!\d+! ($& % 31).','.int $& / 31 !ger,
@moves) =~ s/.{60}\K /\n/gr, "\n";
print $_, "move count: $count\n";</syntaxhighlight>
 
=={{header|Phix}}==
Line 810 ⟶ 1,201:
=={{header|Racket}}==
 
<langsyntaxhighlight lang="racket">#lang racket
(module rules racket/base
(require racket/match)
Line 910 ⟶ 1,301:
p
(define bmp (pict->bitmap p))
(send bmp save-file "images/morpion.png" 'png))</langsyntaxhighlight>
 
 
'''Intermission:''' The <code>render</code> submodule just does drawing, and is not part of the solving. But the <code>main</code> module uses it, so we put it in here:
 
<langsyntaxhighlight lang="racket">(module render racket
(require racket/match
racket/draw
Line 1,014 ⟶ 1,405:
[(hash-has-key? p# k) #\.]
[else #\space])))
(printf "~s~%~a points ~a lines~%" l (hash-count p#) (length l))))</langsyntaxhighlight>
{{out}}
 
Line 1,046 ⟶ 1,437:
<br>This program allows the <tt> D </tt> or <tt> T </tt> forms of the game, and allows any board size (grid size) of three or higher.
<br>The default games is <tt> 5T </tt>
<langsyntaxhighlight lang="rexx">/*REXX program plays Morpion solitaire (with grid output), the default is the 5T version*/
signal on syntax; signal on noValue /*handle possible REXX program errors. */
/* [↓] handle the user options (if any)*/
Line 1,221 ⟶ 1,612:
x= xx
do y=yy-1 by -1; x=x+1; if @.x.y==empty then leave; z= z||@.x.y; end
return ?win(z) /*──────count diag wins: up & <, down & > */</langsyntaxhighlight>
This REXX program makes use of &nbsp; '''LINESIZE''' &nbsp; REXX program (or BIF) which is used to determine the screen width (or linesize) of the terminal (console).
<br>The &nbsp; '''LINESIZE.REX''' &nbsp; REXX program is included here ──► [[LINESIZE.REX]].<br>
Line 1,291 ⟶ 1,682:
* 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.
<syntaxhighlight lang="wren">/* 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()</syntaxhighlight>
<br>
We now embed the above script in the following C program, build and run it.
<syntaxhighlight 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, "%s", 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, "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;
}</syntaxhighlight>
9,476

edits