/* ** C Scripting - Stack Machine */ #define SY__MAIN 1 #include "symbol_set.h" #include "symbol_set.init.h" #include "stack_machine.h" #include #include #include //////////////////////////////// // 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_Unchecked(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); }