Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create simple Tic tac toe game #7

Merged
merged 4 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
"request": "launch",
"mode": "auto",
"program": "${workspaceRoot}/src/main.go",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}",
"args": [
"-f",
"./examples/games/tictactoe.ny"
]
}
]
}
69 changes: 69 additions & 0 deletions examples/games/tictactoe.ny
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
mawu[] board = ["","","","","","","","",""];
mawu currentPlayer = "X";
nambala isRunning = zoona;

ndondomeko printBoard() {
lembanzr("---------");
lembanzr(board[0] + " | " + board[1] + " | " + board[2]);
lembanzr("---------");
lembanzr(board[3] + " | " + board[4] + " | " + board[5]);
lembanzr("---------");
lembanzr(board[6] + " | " + board[7] + " | " + board[8]);
lembanzr("---------");
}

ndondomeko checkWin() {
mawu[] winConditions = [
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6]
];

za (x = 0; x < winConditions.length(); x++) {
mawu condition = winConditions[x];
ngati (board[condition[0]] != "" && board[condition[0]] == board[condition[1]] && board[condition[0]] == board[condition[2]]) {
bweza zoona;
}
}
bweza bodza;
}

ndondomeko move(player, position) {
ngati (board[position] != "") {
bweza bodza;
}

board[position] = player;
ngati (player == "X") {
currentPlayer = "O";
} kapena {
currentPlayer = "X";
}
bweza zoona;
}

ndondomeko playGame() {
pamene(isRunning) {
printBoard();
lembanzr("Player " + currentPlayer + " turn: ");
nambala playerMove = kuNambala(console.landira());
mawu moved = move(currentPlayer, playerMove - 1);
ngati(moved == bodza) {
lembanzr("Invalid move");
lembanzr("Try again");
lembanzr("---------");
// TODO: Implement a way to continue loop
} kapena {
mawu hasWon = checkWin();
ngati (hasWon == zoona) {
lembanzr("Player " + player + " has won!");
lembanzr("Game Over");
isRunning = bodza;
} kapena {
console.fufuta();
}
}
}
}

playGame();
3 changes: 2 additions & 1 deletion src/evaluator/assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package evaluator
import (
"github.com/sevenreup/chewa/src/ast"
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/values"
)

func evaluateAssigment(node *ast.AssigmentStatement, env *object.Environment) object.Object {
Expand Down Expand Up @@ -42,7 +43,7 @@ func evaluateIndexAssignment(node *ast.IndexExpression, val object.Object, env *

if idx >= len(elements) {
for i := len(elements); i <= idx; i++ {
elements = append(elements, NULL)
elements = append(elements, values.NULL)
}

left.Elements = elements
Expand Down
9 changes: 6 additions & 3 deletions src/evaluator/boolean.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package evaluator

import "github.com/sevenreup/chewa/src/object"
import (
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/values"
)

func nativeBoolToBooleanObject(input bool) *object.Boolean {
if input {
return TRUE
return values.TRUE
}
return FALSE
return values.FALSE
}
13 changes: 4 additions & 9 deletions src/evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ import (

"github.com/sevenreup/chewa/src/ast"
"github.com/sevenreup/chewa/src/object"
)

var (
NULL = &object.Null{}
TRUE = &object.Boolean{Value: true}
FALSE = &object.Boolean{Value: false}
"github.com/sevenreup/chewa/src/values"
)

func Eval(node ast.Node, env *object.Environment) object.Object {
Expand Down Expand Up @@ -84,11 +79,11 @@ func Eval(node ast.Node, env *object.Environment) object.Object {

func isTruthy(obj object.Object) bool {
switch obj {
case NULL:
case values.NULL:
return false
case TRUE:
case values.TRUE:
return true
case FALSE:
case values.FALSE:
return false
default:
return true
Expand Down
7 changes: 4 additions & 3 deletions src/evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/sevenreup/chewa/src/lexer"
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/parser"
"github.com/sevenreup/chewa/src/values"
"github.com/shopspring/decimal"
"testing"
)
Expand Down Expand Up @@ -78,7 +79,7 @@ func testLiteralExpression(
}

func testNullObject(t *testing.T, obj object.Object) bool {
if obj != NULL {
if obj != values.NULL {
t.Errorf("object is not NULL. got=%T (%+v)", obj, obj)
return false
}
Expand Down Expand Up @@ -618,8 +619,8 @@ func TestHashLiterals(t *testing.T) {
(&object.String{Value: "two"}).MapKey(): 2,
(&object.String{Value: "three"}).MapKey(): 3,
(&object.Integer{Value: decimal.NewFromInt(4)}).MapKey(): 4,
TRUE.MapKey(): 5,
FALSE.MapKey(): 6,
values.TRUE.MapKey(): 5,
values.FALSE.MapKey(): 6,
}
if len(result.Pairs) != len(expected) {
t.Fatalf("Hash has wrong num of pairs. got=%d", len(result.Pairs))
Expand Down
3 changes: 2 additions & 1 deletion src/evaluator/for_loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package evaluator
import (
"github.com/sevenreup/chewa/src/ast"
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/values"
)

func evalForLoop(node *ast.ForExpression, env *object.Environment) object.Object {
Expand Down Expand Up @@ -59,5 +60,5 @@ func evalForLoop(node *ast.ForExpression, env *object.Environment) object.Object
loop = false
}

return NULL
return values.NULL
}
3 changes: 2 additions & 1 deletion src/evaluator/if.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package evaluator
import (
"github.com/sevenreup/chewa/src/ast"
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/values"
)

func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Object {
Expand All @@ -15,6 +16,6 @@ func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Obje
} else if ie.Alternative != nil {
return Eval(ie.Alternative, env)
} else {
return NULL
return values.NULL
}
}
5 changes: 3 additions & 2 deletions src/evaluator/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package evaluator
import (
"github.com/sevenreup/chewa/src/ast"
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/values"
)

func evalIndexExpression(node *ast.IndexExpression, env *object.Environment) object.Object {
Expand Down Expand Up @@ -31,7 +32,7 @@ func evalArrayIndexExpression(array, index object.Object) object.Object {
maxValue := int64(len(arrayObject.Elements) - 1)

if idx < 0 || idx > maxValue {
return NULL
return values.NULL
}

return arrayObject.Elements[idx]
Expand All @@ -49,7 +50,7 @@ func evaluateMapIndex(node *ast.IndexExpression, left, index object.Object) obje
pair, ok := mapObject.Pairs[key.MapKey()]

if !ok {
return NULL
return values.NULL
}

return pair.Value
Expand Down
19 changes: 11 additions & 8 deletions src/evaluator/prefix.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package evaluator

import "github.com/sevenreup/chewa/src/object"
import (
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/values"
)

func evalPrefixExpression(operator string, right object.Object) object.Object {
switch operator {
Expand All @@ -15,14 +18,14 @@ func evalPrefixExpression(operator string, right object.Object) object.Object {

func evalBangOperatorExpression(right object.Object) object.Object {
switch right {
case TRUE:
return FALSE
case FALSE:
return TRUE
case NULL:
return TRUE
case values.TRUE:
return values.FALSE
case values.FALSE:
return values.TRUE
case values.NULL:
return values.TRUE
default:
return FALSE
return values.FALSE
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/evaluator/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ func evalStringInfixExpression(
return &object.String{Value: leftVal + rightVal}
case "==":
return nativeBoolToBooleanObject(leftVal == rightVal)
case "!=":
return nativeBoolToBooleanObject(leftVal != rightVal)
default:
return newError("unknown operator: %s %s %s",
left.Type(), operator, right.Type())
Expand Down
23 changes: 23 additions & 0 deletions src/library/functions/converters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package functions

import (
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/token"
"github.com/shopspring/decimal"
)

func ParseStringToNumber(env *object.Environment, tok token.Token, args ...object.Object) object.Object {
if len(args) != 1 {
// TODO: Return error dont panic
panic("parse requires exactly one argument")
}

if args[0].Type() != object.STRING_OBJ {
return nil
}

str := args[0].(*object.String).Value
number, _ := decimal.NewFromString(str)

return &object.Integer{Value: number}
}
1 change: 1 addition & 0 deletions src/library/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func init() {

RegisterFunction("lemba", functions.Print)
RegisterFunction("lembanzr", functions.PrintLine)
RegisterFunction("kuNambala", functions.ParseStringToNumber)
}

func RegisterModule(name string, methods map[string]*object.LibraryFunction) {
Expand Down
39 changes: 39 additions & 0 deletions src/library/modules/console.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package modules

import (
"bufio"
"fmt"
"os"
"os/exec"
"runtime"
"strconv"
"strings"

"github.com/sevenreup/chewa/src/values"
"github.com/sevenreup/chewa/src/object"
"github.com/sevenreup/chewa/src/token"
)
Expand All @@ -13,6 +18,8 @@ var ConsoleMethods = map[string]*object.LibraryFunction{}

func init() {
RegisterMethod(ConsoleMethods, "lemba", consolePrint)
RegisterMethod(ConsoleMethods, "fufuta", consoleClear)
RegisterMethod(ConsoleMethods, "landira", consoleRead)
}

func consolePrint(env *object.Environment, tok token.Token, args ...object.Object) object.Object {
Expand All @@ -27,6 +34,38 @@ func consolePrint(env *object.Environment, tok token.Token, args ...object.Objec
return nil
}

func consoleRead(scope *object.Environment, tok token.Token, args ...object.Object) object.Object {
scanner := bufio.NewScanner(os.Stdin)

if len(args) == 1 {
prompt := args[0].(*object.String).Value

fmt.Print(prompt)
}

val := scanner.Scan()

if !val {
return values.NULL
}

return &object.String{Value: scanner.Text()}
}

func consoleClear(scope *object.Environment, tok token.Token, args ...object.Object) object.Object {
if runtime.GOOS == "windows" {
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
cmd.Run()
} else {
cmd := exec.Command("clear")
cmd.Stdout = os.Stdout
cmd.Run()
}

return nil
}

func libPrint(values []string) {
if len(values) > 0 {
str := make([]string, 0)
Expand Down
6 changes: 6 additions & 0 deletions src/object/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ func (e *Environment) Get(name string) (Object, bool) {
return obj, ok
}
func (e *Environment) Set(name string, val Object) Object {
// TODO: Make sure we dont accidentally mutate data that is not in the current scope
_, ok := e.store[name]
if !ok && e.outer != nil {
e.outer.Set(name, val)
return val
}
e.store[name] = val
return val
}
Expand Down
9 changes: 9 additions & 0 deletions src/values/value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package values

import "github.com/sevenreup/chewa/src/object"

var (
NULL = &object.Null{}
TRUE = &object.Boolean{Value: true}
FALSE = &object.Boolean{Value: false}
)
Loading