Task: Morpion solitaire

C: #include <ncurses.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>


int line_len = 5;


int disjoint = 0;

int **board = 0, width, height;

#define for_i for(i = 0; i < height; i++)
#define for_j for(j = 0; j < width; j++)
enum {
	s_blank		= 0,
	s_occupied	= 1 << 0,
	s_dir_ns	= 1 << 1,
	s_dir_ew	= 1 << 2,
	s_dir_ne_sw	= 1 << 3,
	s_dir_nw_se	= 1 << 4,
	s_newly_added	= 1 << 5,
	s_current	= 1 << 6,
};

int irand(int n)
{
	int r, rand_max = RAND_MAX - (RAND_MAX % n);
	while ((r = rand()) >= rand_max);
	return r / (rand_max / n);
}

int** alloc_board(int w, int h)
{
	int i;
	int **buf = calloc(1, sizeof(int *) * h + sizeof(int) * h * w);

	buf[0] = (int*)(buf + h);
	for (i = 1; i < h; i++)
		buf[i] = buf[i - 1] + w;
	return buf;
}


void expand_board(int dw, int dh)
{
	int i, j;
	int nw = width + !!dw, nh = height + !!dh;

	
	int **nbuf = alloc_board(nw, nh);

	dw = -(dw < 0), dh = -(dh < 0);

	for (i = 0; i < nh; i++) {
		if (i + dh < 0 || i + dh >= height) continue;
		for (j = 0; j < nw; j++) {
			if (j + dw < 0 || j + dw >= width) continue;
			nbuf[i][j] = board[i + dh][j + dw];
		}
	}
	free(board);

	board = nbuf;
	width = nw;
	height = nh;
}

void array_set(int **buf, int v, int x0, int y0, int x1, int y1)
{
	int i, j;
	for (i = y0; i <= y1; i++)
		for (j = x0; j <= x1; j++)
			buf[i][j] = v;
}

void show_board()
{
	int i, j;
	for_i for_j mvprintw(i + 1, j * 2,
			(board[i][j] & s_current) ? "X "
			: (board[i][j] & s_newly_added) ? "O "
			: (board[i][j] & s_occupied) ? "+ " : "  ");
	refresh();
}

void init_board()
{
	width = height = 3 * (line_len - 1);
	board = alloc_board(width, height);

	array_set(board, s_occupied, line_len - 1, 1, 2 * line_len - 3, height - 2);
	array_set(board, s_occupied, 1, line_len - 1, width - 2, 2 * line_len - 3);

	array_set(board, s_blank, line_len, 2, 2 * line_len - 4, height - 3);
	array_set(board, s_blank, 2, line_len, width - 3, 2 * line_len - 4);
}

int ofs[4][3] = {
	{0, 1, s_dir_ns},
	{1, 0, s_dir_ew},
	{1, -1, s_dir_ne_sw},
	{1, 1, s_dir_nw_se}
};

typedef struct { int m, s, seq, x, y; } move_t;


void test_postion(int y, int x, move_t * rec)
{
	int m, k, s, dx, dy, xx, yy, dir;
	if (board[y][x] & s_occupied) return;

	for (m = 0; m < 4; m++) { 
		dx = ofs[m][0];
		dy = ofs[m][1];
		dir = ofs[m][2];

		for (s = 1 - line_len; s <= 0; s++) { 
			for (k = 0; k < line_len; k++) {
				if (s + k == 0) continue;

				xx = x + dx * (s + k);
				yy = y + dy * (s + k);
				if (xx < 0 || xx >= width || yy < 0 || yy >= height)
					break;

				
				if (!(board[yy][xx] & s_occupied)) break;

				
				if ((board[yy][xx] & dir)) break;
			}
			if (k != line_len) continue;

			
			if (! irand(++rec->seq))
				rec->m = m, rec->s = s, rec->x = x, rec->y = y;
		}
	}
}

void add_piece(move_t *rec) {
	int dx = ofs[rec->m][0];
	int dy = ofs[rec->m][1];
	int dir= ofs[rec->m][2];
	int xx, yy, k;

	board[rec->y][rec->x] |= (s_current | s_occupied);

	for (k = 0; k < line_len; k++) {
		xx = rec->x + dx * (k + rec->s);
		yy = rec->y + dy * (k + rec->s);
		board[yy][xx] |= s_newly_added;
		if (k >= disjoint || k < line_len - disjoint)
			board[yy][xx] |= dir;
	}
}

int next_move()
{
	int i, j;
	move_t rec;
	rec.seq = 0;

	
	for_i for_j board[i][j] &= ~(s_newly_added | s_current);

	
	for_i for_j test_postion(i, j, &rec);

	
	if (!rec.seq) return 0;

	add_piece(&rec);

	rec.x = (rec.x == width  - 1) ? 1 : rec.x ? 0 : -1;
	rec.y = (rec.y == height - 1) ? 1 : rec.y ? 0 : -1;

	if (rec.x || rec.y) expand_board(rec.x, rec.y);
	return 1;
}

int main()
{
	int ch = 0;
	int move = 0;
	int wait_key = 1;

	init_board();
	srand(time(0));

	initscr();
	noecho();
	cbreak();

	do  {
		mvprintw(0, 0, "Move %d", move++);
		show_board();
		if (!next_move()) {
			next_move();
			show_board();
			break;
		}
		if (!wait_key) usleep(100000);
		if ((ch = getch()) == ' ') {
			wait_key = !wait_key;
			if (wait_key) timeout(-1);
			else timeout(0);
		}
	} while (ch != 'q');

	timeout(-1);
	nocbreak();
	echo();

	endwin();
	return 0;
}




Go: package main

import (
    gc "github.com/rthornton128/goncurses"
    "log"
    "math/rand"
    "time"
)


const (
    lineLen  = 5
    disjoint = 0
)

var (
    board  [][]int
    width  int
    height int
)

const (
    blank    = 0
    occupied = 1 << (iota - 1)
    dirNS
    dirEW
    dirNESW
    dirNWSE
    newlyAdded
    current
)

var ofs = [4][3]int{
    {0, 1, dirNS},
    {1, 0, dirEW},
    {1, -1, dirNESW},
    {1, 1, dirNWSE},
}

type move struct{ m, s, seq, x, y int }

func btoi(b bool) int {
    if b {
        return 1
    }
    return 0
}

func allocBoard(w, h int) [][]int {
    buf := make([][]int, h)
    for i := 0; i < h; i++ {
        buf[i] = make([]int, w)
    }
    return buf
}

func boardSet(v, x0, y0, x1, y1 int) {
    for i := y0; i <= y1; i++ {
        for j := x0; j <= x1; j++ {
            board[i][j] = v
        }
    }
}

func initBoard() {
    height = 3 * (lineLen - 1)
    width = height
    board = allocBoard(width, height)

    boardSet(occupied, lineLen-1, 1, 2*lineLen-3, height-2)
    boardSet(occupied, 1, lineLen-1, width-2, 2*lineLen-3)
    boardSet(blank, lineLen, 2, 2*lineLen-4, height-3)
    boardSet(blank, 2, lineLen, width-3, 2*lineLen-4)
}


func expandBoard(dw, dh int) {
    dw2, dh2 := 1, 1
    if dw == 0 {
        dw2 = 0
    }
    if dh == 0 {
        dh2 = 0
    }
    nw, nh := width+dw2, height+dh2
    nbuf := allocBoard(nw, nh)
    dw, dh = -btoi(dw < 0), -btoi(dh < 0)
    for i := 0; i < nh; i++ {
        if i+dh < 0 || i+dh >= height {
            continue
        }
        for j := 0; j < nw; j++ {
            if j+dw < 0 || j+dw >= width {
                continue
            }
            nbuf[i][j] = board[i+dh][j+dw]
        }
    }
    board = nbuf
    width, height = nw, nh
}

func showBoard(scr *gc.Window) {
    for i := 0; i < height; i++ {
        for j := 0; j < width; j++ {
            var temp string
            switch {
            case (board[i][j] & current) != 0:
                temp = "X "
            case (board[i][j] & newlyAdded) != 0:
                temp = "0 "
            case (board[i][j] & occupied) != 0:
                temp = "+ "
            default:
                temp = "  "
            }
            scr.MovePrintf(i+1, j*2, temp)
        }
    }
    scr.Refresh()
}


func testPosition(y, x int, rec *move) {
    if (board[y][x] & occupied) != 0 {
        return
    }
    for m := 0; m < 4; m++ { 
        dx := ofs[m][0]
        dy := ofs[m][1]
        dir := ofs[m][2]
        var k int
        for s := 1 - lineLen; s <= 0; s++ { 
            for k = 0; k < lineLen; k++ {
                if s+k == 0 {
                    continue
                }
                xx := x + dx*(s+k)
                yy := y + dy*(s+k)
                if xx < 0 || xx >= width || yy < 0 || yy >= height {
                    break
                }

                
                if (board[yy][xx] & occupied) == 0 {
                    break
                }

                
                if (board[yy][xx] & dir) != 0 {
                    break
                }
            }
            if k != lineLen {
                continue
            }

            
            
            rec.seq++
            if rand.Intn(rec.seq) == 0 {
                rec.m, rec.s, rec.x, rec.y = m, s, x, y
            }
        }
    }
}

func addPiece(rec *move) {
    dx := ofs[rec.m][0]
    dy := ofs[rec.m][1]
    dir := ofs[rec.m][2]
    board[rec.y][rec.x] |= current | occupied
    for k := 0; k < lineLen; k++ {
        xx := rec.x + dx*(k+rec.s)
        yy := rec.y + dy*(k+rec.s)
        board[yy][xx] |= newlyAdded
        if k >= disjoint || k < lineLen-disjoint {
            board[yy][xx] |= dir
        }
    }
}

func nextMove() bool {
    var rec move
    
    for i := 0; i < height; i++ {
        for j := 0; j < width; j++ {
            board[i][j] &^= newlyAdded | current
        }
    }

    
    for i := 0; i < height; i++ {
        for j := 0; j < width; j++ {
            testPosition(i, j, &rec)
        }
    }

    
    if rec.seq == 0 {
        return false
    }

    addPiece(&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(rec.x, rec.y)
    }
    return true
}

func main() {
    rand.Seed(time.Now().UnixNano())
    initBoard()
    scr, err := gc.Init()
    if err != nil {
        log.Fatal("init", err)
    }
    defer gc.End()
    gc.Echo(false)
    gc.CBreak(true)
    ch := gc.Key(0)
    move := 0
    waitKey := true
    for {
        scr.MovePrintf(0, 0, "Move %d", move)
        move++
        showBoard(scr)
        if !nextMove() {
            nextMove()
            showBoard(scr)
            break
        }
        if !waitKey {
            time.Sleep(100000 * time.Microsecond)
        }
        if ch = scr.GetChar(); ch == ' ' {
            waitKey = !waitKey
            if waitKey {
                scr.Timeout(-1)
            } else {
                scr.Timeout(0)
            }
        }
        if ch == 'q' {
            break
        }
    }
    scr.Timeout(-1)
    gc.CBreak(false)
    gc.Echo(true)
}