c-scripting/examples/stack_machine.c

526 lines
12 KiB
C

/*
** C Scripting - Stack Machine
*/
#define SY__MAIN 1
#include "symbol_set.h"
#include "symbol_set.init.h"
#include "stack_machine.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
////////////////////////////////
// Little Base Layer
static Arena
arena_alloc(U64 cap){
Arena arena = {0};
arena.memory = malloc(cap);
arena.cap = cap;
return(arena);
}
static void*
arena_push(Arena *arena, U64 size){
void *result = 0;
if (arena->pos + size <= arena->cap){
result = (U8*)arena->memory + arena->pos;
arena->pos += size;
arena->pos = (arena->pos + 7)&~7; // "round up to multiple of 8"
memset(result, 0, size);
}
return(result);
}
static void
arena_pop_to(Arena *arena, U64 pos){
if (pos < arena->pos){
arena->pos = (pos + 7)&~7; // "round up to multiple of 8"
}
}
static U64
arena_pos(Arena *arena){
return(arena->pos);
}
////////////////////////////////
// Stack Machine
//// TICTACTOE Data ////
struct TICTACTOE_Line{ U32 idx[3]; };
static struct TICTACTOE_Line tictactoe_lines[8] = {
{.idx = { 0, 1, 2 }},
{.idx = { 3, 4, 5 }},
{.idx = { 6, 7, 8 }},
{.idx = { 0, 3, 6 }},
{.idx = { 1, 4, 7 }},
{.idx = { 2, 5, 8 }},
{.idx = { 0, 4, 8 }},
{.idx = { 2, 4, 6 }},
};
//// Symbol Definitions ////
SM_FRAME_STRUCT(TicTacToe){
B8 turn;
U8 board[9];
};
SM_FRAME_RETURN_TYPE_DEF(TicTacToe, B32);
SM_FRAME(TicTacToe);
SM_STATE_DECL(TicTacToe, P1Turn);
SM_STATE_DECL(TicTacToe, P2Turn);
SM_STATE_DECL(TicTacToe, BoardCheck);
SM_STATE(TicTacToe, Init){
memset(frame->board, ' ', 9);
sm_state_next(ctx, SyID(SM_STATE, TicTacToe_P1Turn));
}
SM_STATE(TicTacToe, P1Turn){
printf("BOARD:\n");
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[0], frame->board[1], frame->board[2]);
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[3], frame->board[4], frame->board[5]);
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[6], frame->board[7], frame->board[8]);
printf("+-+-+-+\n");
printf("YOUR MOVE: ");
U32 x = 0;
U32 y = 0;
scanf("%u %u", &x, &y);
frame->board[y*3 + x] = 'X';
frame->turn = 1;
sm_state_next(ctx, SyID(SM_STATE, TicTacToe_BoardCheck));
}
SM_STATE(TicTacToe, P2Turn){
U32 x = 0;
U32 y = 0;
struct TICTACTOE_Line *lines = tictactoe_lines;
// if you can win, then win
for (U32 i = 0; i < 8; i += 1){
U32 count_mine = 0;
U32 blank_spot = ~0;
for (U32 j = 0; j < 3; j += 1){
U32 idx = lines[i].idx[j];
U8 c = frame->board[idx];
if (c == 'O'){
count_mine += 1;
}
else if (c == ' '){
blank_spot = idx;
}
}
if (blank_spot != ~0 && count_mine == 2){
x = blank_spot%3;
y = blank_spot/3;
goto play;
}
}
// if you need to block, then block
for (U32 i = 0; i < 8; i += 1){
U32 count_other = 0;
U32 blank_spot = ~0;
for (U32 j = 0; j < 3; j += 1){
U32 idx = lines[i].idx[j];
U8 c = frame->board[idx];
if (c == 'X'){
count_other += 1;
}
else if (c == ' '){
blank_spot = idx;
}
}
if (blank_spot != ~0 && count_other == 2){
x = blank_spot%3;
y = blank_spot/3;
goto play;
}
}
// otherwise just random
{
U32 potentials[9];
U32 count = 0;
for (U32 i = 0; i < 9; i += 1){
if (frame->board[i] == ' '){
potentials[count] = i;
count += 1;
}
}
if (count > 0){
U32 idx = rand()%count;
x = potentials[idx]%3;
y = potentials[idx]/3;
goto play;
}
}
play:
printf("OPPONENT PLAYED AT: %u %u\n", x, y);
frame->board[y*3 + x] = 'O';
frame->turn = 0;
sm_state_next(ctx, SyID(SM_STATE, TicTacToe_BoardCheck));
}
SM_STATE(TicTacToe, P1Win){
printf("You won!\n");
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[0], frame->board[1], frame->board[2]);
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[3], frame->board[4], frame->board[5]);
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[6], frame->board[7], frame->board[8]);
printf("+-+-+-+\n");
sm_state_pop(ctx, &(B32){1});
}
SM_STATE(TicTacToe, P1Lose){
printf("You lost!\n");
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[0], frame->board[1], frame->board[2]);
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[3], frame->board[4], frame->board[5]);
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[6], frame->board[7], frame->board[8]);
printf("+-+-+-+\n");
sm_state_pop(ctx, &(B32){0});
}
SM_STATE(TicTacToe, Tie){
printf("You tied!\n");
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[0], frame->board[1], frame->board[2]);
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[3], frame->board[4], frame->board[5]);
printf("+-+-+-+\n");
printf("|%c|%c|%c|\n", frame->board[6], frame->board[7], frame->board[8]);
printf("+-+-+-+\n");
sm_state_pop(ctx, &(B32){0});
}
SM_STATE(TicTacToe, BoardCheck){
U32 winner = 0;
for (U32 i = 0; i < 8; i += 1){
U32 count[2] = {0};
for (U32 j = 0; j < 3; j += 1){
U32 idx = tictactoe_lines[i].idx[j];
U8 c = frame->board[idx];
if (c == 'X'){
count[0] += 1;
}
else if (c == 'O'){
count[1] += 1;
}
}
for (U32 k = 0; k < 2; k += 1){
if (count[k] == 3){
winner = k + 1;
}
}
}
B32 tie = 0;
if (winner == 0){
tie = 1;
for (U32 i = 0; i < 9; i += 1){
if (frame->board[i] == ' '){
tie = 0;
break;
}
}
}
// end states
if (tie){
sm_state_next(ctx, SyID(SM_STATE, TicTacToe_Tie));
}
else if (winner == 1){
sm_state_next(ctx, SyID(SM_STATE, TicTacToe_P1Win));
}
else if (winner == 2){
sm_state_next(ctx, SyID(SM_STATE, TicTacToe_P1Lose));
}
// next turn
else{
if (frame->turn == 0){
sm_state_next(ctx, SyID(SM_STATE, TicTacToe_P1Turn));
}
else{
sm_state_next(ctx, SyID(SM_STATE, TicTacToe_P2Turn));
}
}
}
SM_FRAME_STRUCT(RockPaperScissors){
S32 score;
};
SM_FRAME_RETURN_TYPE_DEF(RockPaperScissors, B32);
SM_FRAME(RockPaperScissors);
SM_STATE(RockPaperScissors, Play){
printf("YOUR SCORE: %d\n", frame->score);
printf("[1] ROCK [2] PAPER [3] SCISSORS SHOOT: ");
U32 m = 0;
scanf("%u", &m);
m = m%3;
U32 o = rand()%3;
switch (o){
case 1: printf("YOUR OPPONENT THROWS ROCK.\n"); break;
case 2: printf("YOUR OPPONENT THROWS PAPER.\n"); break;
case 0: printf("YOUR OPPONENT THROWS SCISSORS.\n"); break;
}
// p1 wins
if ((m + 2)%3 == o){
frame->score += 1;
if (frame->score < 3){
printf("YOU WON THIS ROUND!\n");
sm_state_stationary(ctx);
}
else{
printf("YOU WIN!\n");
sm_state_pop(ctx, &(B32){1});
}
}
// p2 wins
else if ((o + 2)%3 == m){
frame->score -= 1;
if (frame->score > -3){
printf("YOU LOST THIS ROUND!\n");
sm_state_stationary(ctx);
}
else{
printf("YOU LOST!\n");
sm_state_pop(ctx, &(B32){0});
}
}
// no winner
else{
sm_state_stationary(ctx);
}
}
SM_FRAME_STRUCT(Menu){
U32 wins[2];
U32 last_played_game;
};
SM_FRAME_RETURN_TYPE_DEF(Menu, void);
SM_FRAME(Menu);
SM_STATE_DECL(Menu, RecordWin);
SM_STATE(Menu, Play){
B32 has_win = 0;
for (U32 i = 0; i < sizeof(frame->wins)/sizeof(*frame->wins); i += 1){
if (frame->wins[i]){
has_win = 1;
break;
}
}
if (has_win){
printf("Your wins:\n");
printf("Tic-Tac-Toe %u\n", frame->wins[0]);
printf("Rock-Paper-Scissors %u\n", frame->wins[1]);
}
printf("Choose a game:\n");
printf("[1] Tic-Tac-Toe\n");
printf("[2] Rock-Paper-Scissors\n");
printf("[3] Quit\n");
U32 x = 0;
scanf("%u", &x);
switch (x){
case 1: {
frame->last_played_game = 1;
sm_state_next_push(ctx, SyID(SM_STATE, Menu_RecordWin),
SyID(SM_STATE, TicTacToe_Init));
}break;
case 2: {
frame->last_played_game = 2;
sm_state_next_push(ctx, SyID(SM_STATE, Menu_RecordWin),
SyID(SM_STATE, RockPaperScissors_Play));
}break;
case 3: sm_state_pop(ctx, 0); break;
default: sm_state_stationary(ctx); break;
}
}
SM_STATE(Menu, RecordWin){
B32 *win = sm_ctx_get_prev_return(ctx);
if (win != 0 && *win &&
1 <= frame->last_played_game &&
frame->last_played_game <= sizeof(frame->wins)/sizeof(*frame->wins)){
frame->wins[frame->last_played_game - 1] += 1;
}
sm_state_next(ctx, SyID(SM_STATE, Menu_Play));
}
//// Function Definitions ////
static SM_Ctx
sm_ctx_new(U32 initial_state_id){
SM_Ctx ctx = {0};
ctx.stack_arena = arena_alloc((10 << 20));
sm__stack_push(&ctx, initial_state_id);
return(ctx);
}
static U32
sm_ctx_get_top_state(SM_Ctx *ctx){
U32 result = 0;
if (ctx->stack != 0){
result = ctx->stack->state_id;
}
return(result);
}
static void*
sm_ctx_get_top_frame(SM_Ctx *ctx){
void *result = 0;
if (ctx->stack != 0){
result = ctx->stack->frame;
}
return(result);
}
static B32
sm_ctx_stack_empty(SM_Ctx *ctx){
B32 result = (ctx->stack == 0);
return(result);
}
static void*
sm_ctx_get_prev_return(SM_Ctx *ctx){
void *prev_return = ctx->prev_return;
return(prev_return);
}
static void
sm_state_stationary(SM_Ctx *ctx){
// nothing to do
}
static void
sm_state_next(SM_Ctx *ctx, U32 state_id){
if (ctx->stack != 0){
ctx->stack->state_id = state_id;
}
}
static void
sm_state_stationary_push(SM_Ctx *ctx, U32 push_state_id){
if (ctx->stack != 0){
// push
sm__stack_push(ctx, push_state_id);
}
}
static void
sm_state_next_push(SM_Ctx *ctx, U32 next_state_id, U32 push_state_id){
if (ctx->stack != 0){
// next
ctx->stack->state_id = next_state_id;
// push
sm__stack_push(ctx, push_state_id);
}
}
static void
sm_state_pop(SM_Ctx *ctx, void *return_data){
if (ctx->stack != 0){
// save return
if (return_data != 0){
SM_State *state = SyAddressFromID(SM_STATE, ctx->stack->state_id);
SM_Frame *frame = SyAddressFromID(SM_FRAME, state->frame_id);
ctx->new_return = push_array(ctx->run_arena, U8, frame->return_size);
memcpy(ctx->new_return, return_data, frame->return_size);
}
// stack pops
U64 pos = ctx->stack->pop_pos;
SLLStackPop(ctx->stack);
arena_pop_to(&ctx->stack_arena, pos);
}
}
static void
sm__stack_push(SM_Ctx *ctx, U32 state_id){
SM_State *state = SyAddressFromID(SM_STATE, state_id);
SM_Frame *frame = SyAddressFromID(SM_FRAME, state->frame_id);
U64 pos = arena_pos(&ctx->stack_arena);
SM_StackNode *new_node = push_array(&ctx->stack_arena, SM_StackNode, 1);
new_node->pop_pos = pos;
new_node->frame = push_array(&ctx->stack_arena, U8, frame->size);
new_node->state_id = state_id;
SLLStackPush(ctx->stack, new_node);
}
//// Entry Point ////
int main(void){
sy__run_init();
Arena run_arenas[2] = {0};
for (U32 i = 0; i < 2; i += 1){
run_arenas[i] = arena_alloc((1 << 20));
}
U32 counter = 0;
SM_Ctx ctx = sm_ctx_new(SyID(SM_STATE, Menu_Play));
ctx.diagnostics = 1;
for (;!sm_ctx_stack_empty(&ctx); counter += 1){
ctx.run_arena = &run_arenas[counter&1];
arena_pop_to(ctx.run_arena, 0);
ctx.new_return = 0;
U32 state_id = sm_ctx_get_top_state(&ctx);
SM_State *state = SyAddressFromID(SM_STATE, state_id);
if (state == 0){
printf("ERROR\n");
break;
}
if (ctx.diagnostics){
String8 name = state->name;
printf("DIAGNOSTICS: STATE %.*s\n", (int)name.size, name.str);
}
void *frame = sm_ctx_get_top_frame(&ctx);
state->func(&ctx, frame);
ctx.prev_return = ctx.new_return;
}
return(0);
}