diff --git a/README.md b/README.md index d19d35a..abe8663 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,6 @@ Making a game for linux terminal using pure asm [Linux syscalls reference table](https://syscalls.mebeim.net/?table=x86/64/x64/v6.12) -[synthwave radio 🌌 beats to chill/game to](https://www.youtube.com/watch?v=4xDzrJKXOOY) \ No newline at end of file +[synthwave radio 🌌 beats to chill/game to](https://www.youtube.com/watch?v=4xDzrJKXOOY) + +[nasm instructions manual](http://home.myfairpoint.net/fbkotler/nasmdocc.html#section-A.4.184) \ No newline at end of file diff --git a/game.s b/game.s index 62864d0..d6d524e 100644 --- a/game.s +++ b/game.s @@ -1,18 +1,315 @@ +; TODO out of moves check + global _start section .text -_start: +checkVictory: + mov rax, 0 + + mov cl, [fieldBuffer + 12] + and cl, [fieldBuffer + 14] + and cl, [fieldBuffer + 16] + cmp cl, dil + je .returnTrue + + mov cl, [fieldBuffer + 22] + and cl, [fieldBuffer + 24] + and cl, [fieldBuffer + 26] + cmp cl, dil + je .returnTrue + + mov cl, [fieldBuffer + 32] + and cl, [fieldBuffer + 34] + and cl, [fieldBuffer + 36] + cmp cl, dil + je .returnTrue + + mov cl, [fieldBuffer + 12] + and cl, [fieldBuffer + 22] + and cl, [fieldBuffer + 32] + cmp cl, dil + je .returnTrue + + mov cl, [fieldBuffer + 14] + and cl, [fieldBuffer + 24] + and cl, [fieldBuffer + 34] + cmp cl, dil + je .returnTrue + + mov cl, [fieldBuffer + 16] + and cl, [fieldBuffer + 26] + and cl, [fieldBuffer + 36] + cmp cl, dil + je .returnTrue + + mov cl, [fieldBuffer + 12] + and cl, [fieldBuffer + 24] + and cl, [fieldBuffer + 36] + cmp cl, dil + je .returnTrue + + mov cl, [fieldBuffer + 16] + and cl, [fieldBuffer + 24] + and cl, [fieldBuffer + 32] + cmp cl, dil + je .returnTrue + + mov rax, 0 + ret +.returnTrue: + mov rax, 1 + ret + + +makeAIMove: + rdtsc + mov rbx, rax +.loop: + ; RAND from wiki https://en.wikipedia.org/wiki/Linear_congruential_generator. Numerical Recipes ranqd1, Chapter 7.1, §An Even Quicker Generator, Eq. 7.1.6 parameters from Knuth and H. W. Lewis + mov rdx, 1664525 + mul rdx + add rax, 1013904223 + mov rbx, rax + + ; clamping to 1-3 range and moving to rdi + mov rsi, 3 + mov rax, 0 + mov ah, bh + mov dx, 0 + div si ;TODO for some reason div sil causes division by zero + inc dx + mov di, dx + shl di, 8 + mov rax, 0 + mov ah, bl + mov dx, 0 + div si + inc dx + mov dil, dl + mov rsi, 'o' + call placeChar + cmp rax, 0 + je .return + mov rax, rbx + jmp .loop + +.return: + ret + +drawField: mov rax, 1 ; sys_write mov rdi, 1 ; out - mov rsi, helloThere ; str - mov rdx, helloThereLen ; size + mov rsi, fieldBuffer ; str + mov rdx, fieldBufferLen ; size + syscall ; + ret + + +placeChar: + ;computing pos in string + mov dx, di + + mov rcx, 0 + ; cl = dh * 10 + mov cl, dh + shl cl, 3 + add cl, dh + add cl, dh + + ; cl += dl * 2 + shl dl, 1 + add cl, dl + + ;checking if it was occupied char to the string + mov rax, 0 + mov byte al, [fieldBuffer + rcx] + sub al, ' ' + jne .return + ;placing char to the string + mov byte [fieldBuffer + rcx], sil +.return: + ret + + +readXY: +; clearing input buffer + mov dword [userInputBuffer], 0 + + mov rax, 0 ; sys_read + mov rdi, 0 ; in + mov rsi, userInputBuffer ; str + mov rdx, 256 ; size + syscall ; + + ; only accept strings with length of 4 (reading more to skip some invalid inputs) + cmp rax, 4 + jne .return + + mov ax, 0 + cmp byte [userInputBuffer+1], ' ' ; second should be whitespace + jne .return + cmp byte [userInputBuffer+3], 0xA ; fourth should be \n + jne .return + + mov al, [userInputBuffer] ; x + sub al, '0' + mov ah, [userInputBuffer + 2] ; y + sub ah, '0' +.return: + ret + +_start: + ; call drawField + + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, greetingsMessage ; str + mov rdx, greetingsMessageLen ; size + syscall ; + + call drawField + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, readInputsMessage ; str + mov rdx, readInputsMessageLen ; size syscall ; +.loop: + ;reading input + call readXY + + ;reading check x,y range + cmp al, 1 + jl .printInputError + cmp al, 3 + jg .printInputError + cmp ah, 1 + jl .printInputError + cmp ah, 3 + jg .printInputError + + mov rdi, rax ; passing xy to placeChar function + mov sil, 'x' ; passing char to placeChar function + call placeChar + + ; failed to place a char + jne .printOccupiedError + + ;check win condition + mov rdi, 'x' + call checkVictory + cmp rax, 0 + jne .printYouWon + + ;drawing field again + call drawField + + ;ai thinking... + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, aiThinkingMessage ; str + mov rdx, aiThinkingMessageLen ; size + syscall ; + + mov rbx, 10 +.thinkingReallyHardLoop: + mov rax, 1000000000 +.thinkingReallyHardInnerLoop: + dec rax + jne .thinkingReallyHardInnerLoop + + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, aiThinkingDotMessage ; str + mov rdx, aiThinkingDotMessageLen ; size + syscall ; + dec rbx + jne .thinkingReallyHardLoop + + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, endlnMessage ; str + mov rdx, endlnMessageLen ; size + syscall ; + + call makeAIMove + call drawField + mov rdi, 'o' + call checkVictory + cmp rax, 0 + jne .printYouLost + + ;printing input message again + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, readInputsMessage ; str + mov rdx, readInputsMessageLen ; size + syscall ; + + jmp .loop + +.printInputError: + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, wrongValuesMessage ; str + mov rdx, wrongValuesMessageLen ; size + syscall ; + jmp .loop +.printOccupiedError: + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, occupiedMessage ; str + mov rdx, occupiedMessageLen ; size + syscall ; + jmp .loop + + +.printYouWon: + call drawField + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, youWonMessage ; str + mov rdx, youWonMessageLen ; size + syscall ; + jmp .exit + +.printYouLost: + call drawField + mov rax, 1 ; sys_write + mov rdi, 1 ; out + mov rsi, youLostMessage ; str + mov rdx, youLostMessageLen ; size + syscall ; + +.exit: + ; exiting program mov rax, 60 ; sys_exit mov rdi, 0 ; code syscall ; section .rodata - helloThere: db "Hello there ;)", 0xA, "General Kenobi!!!", 0xA - helloThereLen: equ $ - helloThere \ No newline at end of file + greetingsMessage: db "Welcome to tic-tac-toe!", 0xA + greetingsMessageLen: equ $ - greetingsMessage + readInputsMessage: db "Type [x y] to place a cross:", 0xA + readInputsMessageLen: equ $ - readInputsMessage + wrongValuesMessage: db "Unexpected x or y value. Could be 1,2 or 3 in [x y] format. Try again:", 0xA + wrongValuesMessageLen: equ $ - wrongValuesMessage + occupiedMessage: db "Cell is already occupied. Try again:", 0xA + occupiedMessageLen: equ $ - occupiedMessage + youWonMessage: db "YOU WON!!!", 0xA + youWonMessageLen: equ $ - youWonMessage + youLostMessage: db "you lost T_T", 0xA + youLostMessageLen: equ $ - youLostMessage + aiThinkingMessage: db "AI thinking" + aiThinkingMessageLen: equ $ - aiThinkingMessage + aiThinkingDotMessage: db "." + aiThinkingDotMessageLen: equ $ - aiThinkingDotMessage + endlnMessage: db 0xA + endlnMessageLen: equ $ - endlnMessage + +section .bss + userInputBuffer resb 256 +section .data + fieldBuffer: db "#########", 0xA, "# #", 0xA, "# #", 0xA, "# #", 0xA, "#########", 0xA + fieldBufferLen: equ $ - fieldBuffer \ No newline at end of file