Csp

Arcade

This page is to journal my construction of the arcade.

Technology stack

Frontend:

  • React with Typescript
  • Tailwind CSS
  • Zustand (simple state management)
  • Framer Motion (smooth animations)

Backend:

  • Node.js with express
  • Rest API

Steps

mkdir -p backend/src/controllers backend/src/models backend/src/routes
mkdir -p frontend/src/components/games frontend/src/pages frontend/public
  1. create package.json for both backend and frontend
  2. create main backend file, game routes, game controller
  3. pm2 configuration for containerisation
  4. create tailwind config
  5. create App.js in frontend

Bytelocker in C

I loved locking up folders as a kid on the Windows 7 computers using batch scripts I didn't understand.

This is a small program written in C to help me consolidate by understanding of `FILE` streams, bitwise operations and memory representations of data.

## What does bytelocker do?

Bytelocker takes in 2 arguments, a file and a password. It then encrypts this file in place with an ECB cipher.

## Why is this useful?

Read more >

Connect 4

# init
true  = 1
false = 0
CONNECT = 4
MIN_BOARD_DIMENSION = 4
MAX_BOARD_WIDTH     = 9
MAX_BOARD_HEIGHT    = 16
CELL_EMPTY  = '.'
CELL_RED    = 'R'
CELL_YELLOW = 'Y'
WINNER_NONE   = 0
WINNER_RED    = 1
WINNER_YELLOW = 2
TURN_RED    = 0
TURN_YELLOW = 1

	.data

# char board[MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
board:		.space  MAX_BOARD_HEIGHT * MAX_BOARD_WIDTH

board_width:	.word 0

board_height:	.word 0


enter_board_width_str:	.asciiz "Enter board width: "
enter_board_height_str: .asciiz "Enter board height: "
game_over_draw_str:	.asciiz "The game is a draw!\n"
game_over_red_str:	.asciiz "Game over, Red wins!\n"
game_over_yellow_str:	.asciiz "Game over, Yellow wins!\n"
board_too_small_str_1:	.asciiz "Board dimension too small (min "
board_too_small_str_2:	.asciiz ")\n"
board_too_large_str_1:	.asciiz "Board dimension too large (max "
board_too_large_str_2:	.asciiz ")\n"
red_str:		.asciiz "[RED] "
yellow_str:		.asciiz "[YELLOW] "
choose_column_str:	.asciiz "Choose a column: "
invalid_column_str:	.asciiz "Invalid column\n"
no_space_column_str:	.asciiz "No space in that column!\n"
debug:	.asciiz "Debug:\n"

	.text
main:
	# Args:     void
	# Returns:
	#   - $v0: int
	#
	# Frame:    [$ra, ...]
	# Uses:     [...]
	# Clobbers: [...]
	#
	# Locals:
	#   - [...]
	#
	# Structure:
	#   main
	#   -> [prologue]
	#   -> body
	#   -> [epilogue]

main__prologue:
	begin			# begin a new stack frame
	push	$ra		# | $ra

main__body:
	# check valid board dimensions
	
	# print width string
	la		$a0, enter_board_width_str
	li		$v0, 4
	syscall

	# read and store width integer into board_width
	li		$v0, 5	# read integer
	syscall	
	la		$t0, board_width
	sw		$v0, ($t0)
	move	$a0, $v0
	li		$a1, MIN_BOARD_DIMENSION
	li		$a2, MAX_BOARD_WIDTH
	jal		assert_board_dimension

	# print height string
	la		$a0, enter_board_height_str
	li		$v0, 4
	syscall
	
	# read and store height integer into board_height
	li		$v0, 5
	syscall
	la		$t1, board_height
	sw		$v0, ($t1)
	move	$a0, $v0
	li		$a1, MIN_BOARD_DIMENSION
	li		$a2, MAX_BOARD_HEIGHT
	jal		assert_board_dimension

	# initialise board
	jal		initialise_board

	# print board
	jal		print_board
	
	# play game
	jal		play_game


main__epilogue:
	pop	$ra		# | $ra
	end			# ends the current stack frame

	li	$v0, 0
	jr	$ra		# return 0;


########################################################################
# .TEXT <assert_board_dimension>
	.text
assert_board_dimension:
	# Args:
	#   - $a0: int dimension
	#   - $a1: int min
	#   - $a2: int max
	# Returns:  void
	#
	# Frame:    [...]
	# Uses:     [...]
	# Clobbers: [...]
	#
	# Locals:
	#   - [...]
	#
	# Structure:
	#   assert_board_dimension
	#   -> [prologue]
	#   -> body
	#   -> [epilogue]

assert_board_dimension__prologue:
	begin
	push	$ra
assert_board_dimension__body:
	blt		$a0, $a1, assert_board_dimension__small
	bgt		$a0, $a2, assert_board_dimension__large

assert_board_dimension__epilogue:
	pop		$ra
	end
	jr	$ra		# return;

assert_board_dimension__small:
	la		$a0, board_too_small_str_1
	li		$v0, 4
	syscall
	li		$a0, 4
	li		$v0, 1
	syscall
	la		$a0, board_too_small_str_2
	li		$v0, 4
	syscall
	
	# returns with error code 1
	li		$a0, 1
	li		$v0, 17
	syscall


assert_board_dimension__large:
	la		$a0, board_too_large_str_1
	li		$v0, 4
	syscall
	li		$a0, 16
	li		$v0, 1
	syscall
	la		$a0, board_too_large_str_2
	li		$v0, 4
	syscall
	
	# returns with error code 1
	li		$a0, 1
	li		$v0, 17
	syscall
	
	#j		assert_board_dimension__epilogue


########################################################################
# .TEXT <initialise_board>
	.text
initialise_board:
	# Args:     void
	# Returns:  void
	#
	# Frame:    [...]
	# Uses:     [...]
	# Clobbers: [...]
	#
	# Locals:
	#   - [...]
	#
	# Structure:
	#   initialise_board
	#   -> [prologue]
	#   -> body
	#   -> [epilogue]

initialise_board__prologue:
	begin
	push	$ra

initialise_board__body:
	li		$s0, 0		# row
	la		$t0, board
	lw		$t6, board_width
	lw		$t7, board_height
	
initialise_board__row_loop:
	bge		$s0, $t7, initialise_board__row_end
	li		$s1, 0		# col

initialise_board__col_loop:
	bge		$s1, $t6, initialise_board__col_end
	# adding row offset
	mul		$t1, $s0, MAX_BOARD_WIDTH
	
	# adding col offset
	add		$t3, $t1, $s1

	li		$t4, CELL_EMPTY
	sb		$t4, board($t3) 
	
	addi	$s1, $s1, 1
	b		initialise_board__col_loop
	
initialise_board__col_end:
	addi	$s0, $s0, 1
	b		initialise_board__row_loop
	
initialise_board__row_end:
	# clearing variables
	li		$s0, 0
	li		$s1, 0
	

initialise_board__epilogue:
	pop	$ra
	end
	jr	$ra		# return;


########################################################################
# .TEXT <play_game>
	.text
play_game:
	# Args:     void
	# Returns:  void
	#
	# Frame:    [...]
	# Uses:     [...]
	# Clobbers: [...]
	#
	# Locals:
	#   - [...]
	#
	# Structure:
	#   play_game
	#   -> [prologue]
	#   -> body
	#   -> [epilogue]

play_game__prologue:
	begin
	push $ra
play_game__body:
	li		$s4, TURN_RED
	li		$s3, WINNER_NONE
	
play_game__loop:
	bne		$s3, WINNER_NONE, play_game__check
	jal		is_board_full
	beq		$v0, true, play_game__check 
	
	move	$a0, $s4
	jal		play_turn
	
	move	$s4, $v0 
	
	jal		print_board
	
	jal		check_winner
	move	$s3, $v0
	
	b		play_game__loop


play_game__check:
	li		$t0, WINNER_NONE
	li		$t1, WINNER_RED
	li		$t2, WINNER_YELLOW
	beq		$s3, $t0, play_game__draw
	beq		$s3, $t1, play_game__red
	beq		$s3, $t2, play_game__yellow


play_game__draw:
	la		$a0, game_over_draw_str
	li		$v0, 4
	syscall
	
	j		play_game__epilogue


play_game__red:
	la		$a0, game_over_red_str
	li		$v0, 4
	syscall

	j		play_game__epilogue

play_game__yellow:
	la		$a0, game_over_yellow_str
	li		$v0, 4
	syscall

	j		play_game__epilogue

play_game__epilogue:
	pop $ra
	end
	jr	$ra		# return;


########################################################################
# .TEXT <play_turn>
	.text
play_turn:
	# Args:
	#   - $a0: int whose_turn
	# Returns:  void
	#
	# Frame:    [...]
	# Uses:     [...]
	# Clobbers: [...]
	#
	# Locals:
	#   - [...]
	#
	# Structure:
	#   play_turn
	#   -> [prologue]
	#   -> body
	#   -> [epilogue]

play_turn__prologue:
	begin
	push $ra
	lb		$t0, board_width
play_turn__prints:
	move	$s0, $a0
	beq		$s0, TURN_RED, play_turn__print_red
	beq		$s0, TURN_YELLOW, play_turn__print_yellow
	
play_turn__print_red:
	la		$a0, red_str
	li		$v0, 4
	syscall
	
	j		play_turn__body
play_turn__print_yellow:
	la		$a0, yellow_str
	li		$v0, 4
	syscall

	j		play_turn__body
play_turn__body:
	la		$a0, choose_column_str
	li		$v0, 4
	syscall
	
	li		$v0, 5
	syscall
	
	move	$s1, $v0
	sub		$s1, $s1, 1			# user input 1-indexed (column)
	
	blt		$s1, $zero, play_turn__invalid
	bge		$s1, $t0, play_turn__invalid

	lb		$s2, board_height
	sub		$s2, $s2, 1			# row
	
play_turn__loop:
	blt		$s2, 0, play_turn__body_cont
	
	li		$t7, MAX_BOARD_WIDTH
	mul		$t0, $s2, $t7
	add		$t0, $t0, $s1
	lb		$t2, board($t0)
	beq		$t2, 46, play_turn__body_cont
	
	sub		$s2, $s2, 1
	
	blt		$s2, 0, play_turn__full

	b		play_turn__loop

play_turn__body_cont:
	beq		$s0, TURN_RED, play_turn__set_red
	beq		$s0, TURN_YELLOW, play_turn__set_yellow
	
play_turn__set_red:
	mul		$t0, $s2, MAX_BOARD_WIDTH
	add		$t0, $s1
	
	li		$t1, CELL_RED
	sb		$t1, board($t0)
	
	
	li		$s0, TURN_YELLOW
	j		play_turn__epilogue

play_turn__set_yellow:
	mul		$t0, $s2, MAX_BOARD_WIDTH
	add		$t0, $s1
	
	li		$t1, CELL_YELLOW
	sb		$t1, board($t0)

	li		$s0, TURN_RED
	j		play_turn__epilogue

play_turn__invalid:
	la		$a0, invalid_column_str
	li		$v0, 4
	syscall
	
	j		play_turn__epilogue
	
play_turn__full:
	la		$a0, no_space_column_str
	li		$v0, 4
	syscall
	
	j		play_turn__epilogue

play_turn__epilogue:
	la	$v0, ($s0)
	pop	$ra
	end
	jr	$ra		# return;


########################################################################
# .TEXT <check_winner>
	.text
check_winner:
	# Args:	    void
	# Returns:
	#   - $v0: int
	#
	# Frame:    [...]
	# Uses:     [...]
	# Clobbers: [...]
	#
	# Locals:
	#   - [...]
	#
	# Structure:
	#   check_winner
	#   -> [prologue]
	#   -> body
	#   -> [epilogue]

check_winner__prologue:
	begin
	push $ra

check_winner__body:
	li		$s0, 0		# row
	lw		$t6, board_width
	lw		$t7, board_height

check_winner__row_loop:
	bge		$s0, $t7, check_winner__row_end
	li		$s1, 0		# col
	
check_winner__col_loop:
	bge		$s1, $t6, check_winner__col_end
	
	# computing row offset
	mul		$t2, $s0, MAX_BOARD_WIDTH
	# computing col offset
	add		$t2, $s1
	
	## check vertical line
	move	$a0, $s0
	move	$a1, $s1
	li		$a2, 1
	li		$a3, 0
	
	jal		check_line
	bne		$v0, WINNER_NONE, check_winner__epilogue
	
	# check horizontal line
	move	$a0, $s0
	move	$a1, $s1
	li		$a2, 0
	li		$a3, 1
	
	jal		check_line
	bne		$v0, WINNER_NONE, check_winner__epilogue

	# check gradient = -1 line
	move	$a0, $s0
	move	$a1, $s1
	li		$a2, 1
	li		$a3, 1
	
	jal		check_line
	bne		$v0, WINNER_NONE, check_winner__epilogue
	
	# check gradient = 1 line
	move	$a0, $s0
	move	$a1, $s1
	li		$a2, 1
	li		$a3, -1
	
	jal		check_line
	bne		$v0, WINNER_NONE, check_winner__epilogue
	
	addiu	$s1, 1
	j		check_winner__col_loop
	
check_winner__col_end:
	addiu	$s0, 1
	j		check_winner__row_loop
	
check_winner__row_end:

check_winner__epilogue:
	pop $ra
	end
	jr	$ra		# return;


########################################################################
# .TEXT <check_line>
	.text
check_line:
	# Args:
	#   - $a0: int start_row
	#   - $a1: int start_col
	#   - $a2: int offset_row
	#   - $a3: int offset_col
	# Returns:
	#   - $v0: int
	#
	# Frame:    [...]
	# Uses:     [...]
	# Clobbers: [...]
	#
	# Locals:
	#   - [...]
	#
	# Structure:
	#   check_line
	#   -> [prologue]
	#   -> body
	#   -> [epilogue]

check_line__prologue:
	begin
	push	$ra

	lw		$s2, board_height
	lw		$s5, board_width

	# calculating offset
	mul		$t0, $a0, MAX_BOARD_WIDTH
	add		$t0, $t0, $a1		# first_cell
	
	lb		$t1, board($t0)
	beq		$t1, CELL_EMPTY, check_line__winner_none
	
	add		$t2, $a0, $a2		# row
	add		$t3, $a1, $a3		# col

	# how many connects required
	li		$t5, CONNECT
	sub		$t5, $t5, 1
	
	# i for loop
	li		$t4, 0
	
	# clearing t7 register
	li		$t8, 0

check_line__body:
	bge		$t4, $t5, check_line__body_cont
	
	blt		$t2, $zero, check_line__winner_none
	blt		$t3, $zero, check_line__winner_none
	
	bge		$t2, $s2, check_line__winner_none
	bge		$t3, $s5, check_line__winner_none
	
	mul		$t8, $t2, MAX_BOARD_WIDTH
	add		$t8, $t8, $t3		# board[row][col]

	lb		$t9, board($t8)
	bne		$t1, $t9, check_line__winner_none

	add		$t2, $t2, $a2
	add		$t3, $t3, $a3
	
	addiu	$t4, 1
	b		check_line__body		
	

check_line__winner_none:
	li		$v0, WINNER_NONE
	j		check_line__epilogue

check_line__winner_red:
	li		$v0, WINNER_RED
	j		check_line__epilogue

check_line__winner_yellow:
	li		$v0, WINNER_YELLOW
	j		check_line__epilogue
	
check_line__body_cont:
	beq		$t1, CELL_RED, check_line__winner_red
	beq		$t1, CELL_YELLOW, check_line__winner_yellow

check_line__epilogue:
	pop	$ra
	end
	jr	$ra		# return;




########################################################################
# .TEXT <is_board_full>
# YOU DO NOT NEED TO CHANGE THE IS_BOARD_FULL FUNCTION
	.text
is_board_full:
	# Args:     void
	# Returns:
	#   - $v0: bool
	#
	# Frame:    []
	# Uses:     [$v0, $t0, $t1, $t2, $t3]
	# Clobbers: [$v0, $t0, $t1, $t2, $t3]
	#
	# Locals:
	#   - $t0: int row
	#   - $t1: int col
	#
	# Structure:
	#   is_board_full
	#   -> [prologue]
	#   -> body
	#   -> loop_row_init
	#   -> loop_row_cond
	#   -> loop_row_body
	#     -> loop_col_init
	#     -> loop_col_cond
	#     -> loop_col_body
	#     -> loop_col_step
	#     -> loop_col_end
	#   -> loop_row_step
	#   -> loop_row_end
	#   -> [epilogue]

is_board_full__prologue:
is_board_full__body:
	li	$v0, true

is_board_full__loop_row_init:
	li	$t0, 0						# int row = 0;

is_board_full__loop_row_cond:
	lw	$t2, board_height
	bge	$t0, $t2, is_board_full__epilogue		# if (row >= board_height) goto is_board_full__loop_row_end;

is_board_full__loop_row_body:
is_board_full__loop_col_init:
	li	$t1, 0						# int col = 0;

is_board_full__loop_col_cond:
	lw	$t2, board_width
	bge	$t1, $t2, is_board_full__loop_col_end		# if (col >= board_width) goto is_board_full__loop_col_end;

is_board_full__loop_col_body:
	mul	$t2, $t0, MAX_BOARD_WIDTH			# row * MAX_BOARD_WIDTH
	add	$t2, $t2, $t1					# row * MAX_BOARD_WIDTH + col
	lb	$t3, board($t2)					# board[row][col];
	bne	$t3, CELL_EMPTY, is_board_full__loop_col_step	# if (cell != CELL_EMPTY) goto is_board_full__loop_col_step;

	li	$v0, false
	b	is_board_full__epilogue				# return false;

is_board_full__loop_col_step:
	addi	$t1, $t1, 1					# col++;
	b	is_board_full__loop_col_cond			# goto is_board_full__loop_col_cond;

is_board_full__loop_col_end:
is_board_full__loop_row_step:
	addi	$t0, $t0, 1					# row++;
	b	is_board_full__loop_row_cond			# goto is_board_full__loop_row_cond;

is_board_full__loop_row_end:
is_board_full__epilogue:
	jr	$ra						# return;


########################################################################
# .TEXT <print_board>
# YOU DO NOT NEED TO CHANGE THE PRINT_BOARD FUNCTION
	.text
print_board:
	# Args:     void
	# Returns:  void
	#
	# Frame:    []
	# Uses:     [$v0, $a0, $t0, $t1, $t2]
	# Clobbers: [$v0, $a0, $t0, $t1, $t2]
	#
	# Locals:
	#   - `int col` in $t0
	#   - `int row` in $t0
	#   - `int col` in $t1
	#
	# Structure:
	#   print_board
	#   -> [prologue]
	#   -> body
	#   -> for_header_init
	#   -> for_header_cond
	#   -> for_header_body
	#   -> for_header_step
	#   -> for_header_post
	#   -> for_row_init
	#   -> for_row_cond
	#   -> for_row_body
	#     -> for_col_init
	#     -> for_col_cond
	#     -> for_col_body
	#     -> for_col_step
	#     -> for_col_post
	#   -> for_row_step
	#   -> for_row_post
	#   -> [epilogue]

print_board__prologue:
print_board__body:
	li	$v0, 11			# syscall 11: print_int
	la	$a0, '\n'
	syscall				# printf("\n");

print_board__for_header_init:
	li	$t0, 0			# int col = 0;

print_board__for_header_cond:
	lw	$t1, board_width
	blt	$t0, $t1, print_board__for_header_body	# col < board_width;
	b	print_board__for_header_post

print_board__for_header_body:
	li	$v0, 1			# syscall 1: print_int
	addiu	$a0, $t0, 1		#              col + 1
	syscall				# printf("%d", col + 1);

	li	$v0, 11			# syscall 11: print_character
	li	$a0, ' '
	syscall				# printf(" ");

print_board__for_header_step:
	addiu	$t0, 1			# col++
	b	print_board__for_header_cond

print_board__for_header_post:
	li	$v0, 11
	la	$a0, '\n'
	syscall				# printf("\n");

print_board__for_row_init:
	li	$t0, 0			# int row = 0;

print_board__for_row_cond:
	lw	$t1, board_height
	blt	$t0, $t1, print_board__for_row_body	# row < board_height
	b	print_board__for_row_post

print_board__for_row_body:
print_board__for_col_init:
	li	$t1, 0			# int col = 0;

print_board__for_col_cond:
	lw	$t2, board_width
	blt	$t1, $t2, print_board__for_col_body	# col < board_width
	b	print_board__for_col_post

print_board__for_col_body:
	mul	$t2, $t0, MAX_BOARD_WIDTH
	add	$t2, $t1
	
	## debug
	#la	$a0, ($t2)
	#li	$v0, 1
	#syscall
	## debug

	lb	$a0, board($t2)		# board[row][col]

	li	$v0, 11			# syscall 11: print_character
	syscall				# printf("%c", board[row][col]);
	
	li	$v0, 11			# syscall 11: print_character
	li	$a0, ' '
	syscall				# printf(" ");

print_board__for_col_step:
	addiu	$t1, 1			# col++;
	b	print_board__for_col_cond

print_board__for_col_post:
	li	$v0, 11			# syscall 11: print_character
	li	$a0, '\n'
	syscall				# printf("\n");

print_board__for_row_step:
	addiu	$t0, 1
	b	print_board__for_row_cond

print_board__for_row_post:
print_board__epilogue:
	jr	$ra			# return;