Skip to content
Open
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
23 changes: 22 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,25 @@ build
# VS Cache
.vs/
.vscode/
.idea/
.idea/

### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Go workspace file
go.work
go.work.sum

# env file
.env
40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ auch verschiedene Starthilfen zur Verfügung stellen.
## Aufgabe

Es ist ein **Bot** zu entwickeln, der nach den **Standardregeln** das Spiel "4 Gewinnt" spielt. Hierfür haben wir eine
Schnittstelle in den jeweiligen Clients (Java oder Python) bereit gestellt, welche implementiert werden muss. Das Ziel
Schnittstelle in den jeweiligen Clients (Java, Python oder Go) bereit gestellt, welche implementiert werden muss. Das Ziel
ist es eine gewisse Anzahl an Spiele gegen eine Gegner KI zu gewinnen und als Turniersieger hervorzugehen! :)

## Teilnahmebedingungen

- Implementierung der vorgegebenen Bot Schnittstelle (Java oder Python)
- Implementierung der vorgegebenen Bot Schnittstelle (Java, Python oder Go)
- Der Bot sendet dem Spiel vor dem Timeout (ca 2 Sekunden), was der nächste Zug ist
- Die KI ist selbst geschrieben

Expand All @@ -31,6 +31,7 @@ ist es eine gewisse Anzahl an Spiele gegen eine Gegner KI zu gewinnen und als Tu
- Python 3.10.4+ (für den Spieleserver und gegebenfalls für den Bot)
- Java SDK 17+ (falls der Bot in Java geschrieben wird)
- dotnet 8.0 (falls der Bot in csharp geschrieben wird)
- Go 1.23.5+ (falls der Bot in Go geschrieben wird)

## Den Spieleserver starten

Expand Down Expand Up @@ -137,6 +138,21 @@ Port | -p or --port | Port des 4 Connect Servers auf dem Zielserver | 87
Wenn ihr also mehrere Bots geschrieben habt, könnt ihr mit dem Namen das ganze umschalten.
Es ist aber natürlich auch ausreichend den Default zu belassen und alles im "UserBot" zu machen.

## Einen Client mit dem Server verbinden (Go)

Den Go Client starten
```bash
cd goClient
go run cmd/main.go
```
| Parmeter | Switch | Beschreibung | Default |
| -------- | -------| --------------------------------------------- | --------|
|BotName | -bot | Name der KI, die gestartet werden soll | MyBot |
|Port | -port | Port des 4 Connect Servers auf dem Zielserver | 8765 |

Wenn ihr also mehrere Bots geschrieben habt, könnt ihr mit dem Namen das ganze umschalten.
Es ist aber natürlich auch ausreichend den Default zu belassen und alles im "MyBot" zu machen.

## Einen Client mit dem Server verbinden (Manueller Client)

Um gegen deine eigene KI spielen zu können, kannst du einen manuellen Client starten.
Expand Down Expand Up @@ -219,6 +235,26 @@ public int Play(int[][] field)
Der Returnwert der Play Methode ist ein Integer innerhalb von 0-5 (mögliche Spalten im Spielfeld)


### Go

Unter dem Pfad `goClient/internal/bot` findest du die Datei `mybot.go`.
Diese Klasse beinhaltet die User-KI und die folgende Funktion `Run()`:

```go
func (b *MyBot) Run(state *model.StateData) int {

//
// Implement your logic here so that the bot can play
//

// Currently, the first column is always selected as the next move
return 0
}
```

Der Returnwert der `Run()`-Funktion ist ein Integer innerhalb von 0-5 (mögliche
Spalten im Spielfeld).


## Spielfeld Daten

Expand Down
95 changes: 95 additions & 0 deletions goClient/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# connect4 Tournament - Go Client

## Overview

This is a Go client for the connect4 game.

## Installation

### Prerequisites

- Go 1.23.5 or higher
- github.com/gorilla/websocket v1.5.3 or higher

### Steps to Install

1. **Install Dependencies**

Make sure to install the necessary Go dependencies. You can do this by
running:

```bash
go mod tidy
```

2. **Build the Bot**

To compile the bot, simply run:

```bash
(on Linux)
go build -o connect4-bot cmd/main.go

(on Windows)
go build -o connect4-bot.exe cmd/main.go
```

3. **Start the Bot**

You can now start the bot with the following command:

```bash
(on Linux)
./connect4-bot

(on Windows)
connect4-bot.exe
```

**Configuration**

You can configure the bot by passing arguments when starting the bot:

```bash
(on Linux)
./connect4-bot --port 8765 --bot RandomBot

(on Windows)
connect4-bot.exe --port 8765 --bot RandomBot
```

- **`--port`**: Port where the game server is running (default: `8765`).
- **`--bot`**: Choose the type of bot to use. Possible values:
- `"RandomBot"`: Example bot implementation which plays random
- `"MyBot"`: Template for your custom bot

## Create Your Own Bot

1. Create a copy of `mybot.go` in the `internal/bot` directory (e.g.
`internal/bot/mycustombot.go`)
2. Rename all instances of `MyBot` in your new file to your bot's name
(e.g. `MyCustomBot`)
3. Implement your bot logic in the `Run()` method
4. Add your bot in the `internal/bot/factory.go` file in the `NewBot()` method
5. Run the client with your bot's name using the `--bot MyCustomBot` flag

## Project Structure

```
│ go.mod
│ go.sum
│ README.md
├───cmd
│ main.go // Main entry point for the application
└───internal
├───bot
│ bot.go // Base implementation of the bot
│ factory.go // Bot factory for creating bots
│ mybot.go // Template for creating a custom bot
│ randombot.go // Example bot implementation
├───model
│ request.go // Defines the requests for the gameserver
│ response.go // Defines the responses from the gameserver
└───websocket
client.go // WebSocket client for communication
```
51 changes: 51 additions & 0 deletions goClient/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"flag"
"fmt"
"log"

"connect4-bot/internal/bot"
"connect4-bot/internal/websocket"
)

func main() {
// Define command-line arguments
port := flag.Int("port", 8765, "Port on which the game server listens")
botType := flag.String("bot", "MyBot", "Name of the bot to be used")

// Parse the command-line arguments
flag.Parse()

// Print the port and selected bot information
fmt.Printf("Server listens on port: %d\n", *port)
fmt.Printf("Selected bot : %s\n", *botType)

// Use the BotFactory to create the desired bot
factory := &bot.BotFactory{}
myBot, err := factory.NewBot(*botType)
if err != nil {
log.Fatal("Error creating the bot:", err)
}

// Create and connect the WebSocket client
client := websocket.NewClient(myBot, *port)

err = client.Connect()
if err != nil {
log.Fatal("Connection failed:", err)
} else {
log.Println("Client ID :", client.ClientId)
}

// check if we have a valid connection
if client.ClientId == 0 {
log.Fatal("Connection failed.")
}

// Start goroutines
go client.Listen() // Listen for WebSocket messages and call the bot

// Block the main thread to keep the program running
select {}
}
5 changes: 5 additions & 0 deletions goClient/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module connect4-bot

go 1.23.5

require github.com/gorilla/websocket v1.5.3
2 changes: 2 additions & 0 deletions goClient/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
25 changes: 25 additions & 0 deletions goClient/internal/bot/bot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package bot

import "connect4-bot/internal/model"

// Bot is the interface for all bot types.
// Any bot that implements this interface must define two methods:
// 1. Run: Makes the next move
// 2. GetName: Returns the bot's name.
type Bot interface {
// Run is the method that contains the logic for the bot's actions.
// It takes the current game state as input and returns an integer
// indicating the column where the coin has to go.
// The specific behavior of the bot depends on the implementation of this
// method in the concrete bot.
Run(state *model.StateData) int

// GetName returns the name of the bot.
// This name is used for two purposes:
// 1. To create the websocket URL for communication with the game server.
// 2. To display the bot's name within the game.
// Each bot will have its own unique name (e.g. "RandomBot").
// This allows the game to know which bot is being used and show the correct
// name.
GetName() string
}
24 changes: 24 additions & 0 deletions goClient/internal/bot/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package bot

import "errors"

// BotFactory provides a function to create various types of bots
// This factory pattern allows for easy addition of new bot types in the future.
type BotFactory struct{}

// NewBot creates a new bot based on the given name.
// It returns a specific bot implementation or an error if the bot type is unknown.
func (f *BotFactory) NewBot(name string) (Bot, error) {
// Use a switch statement to decide which type of bot to create based on the provided name
switch name {
case "RandomBot":
// Create and return the bot which fill random columns
return &RandomBot{}, nil
case "MyBot":
// Create and return the bot template bot
return &MyBot{}, nil
default:
// Return an error if the bot name is unknown
return nil, errors.New("unknown bot type: " + name)
}
}
28 changes: 28 additions & 0 deletions goClient/internal/bot/mybot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package bot

import (
"connect4-bot/internal/model"
)

// MyBot implements the Bot interface for an template bot.
type MyBot struct {
}

// Run processes the current game status and determines in which column the coin
// should be inserted.
// In this implementation, the bot simply does nothing and return always a zero.
func (b *MyBot) Run(state *model.StateData) int {

//
// Implement your logic here so that the bot can play
//

// Currently, the first column is always selected as the next move
return 0
}

// GetName returns the name of the bot. This name is used for creating the
// WebSocket URL and displaying the bot's name within the game.
func (b *MyBot) GetName() string {
return "MyBot"
}
27 changes: 27 additions & 0 deletions goClient/internal/bot/randombot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package bot

import (
"connect4-bot/internal/model"
"math/rand"
"time"
)

// RandomBot implements the Bot interface for an random playing bot.
type RandomBot struct {
}

// Run processes the current game status and determines in which column the coin
// should be inserted.
// In this implementation, the bot simply insert the coin in random columns.
func (b *RandomBot) Run(state *model.StateData) int {
// generate a seed for the random generator
rand.New(rand.NewSource(time.Now().UnixNano()))
// return a random column number
return rand.Intn(len(state.Field[0]))
}

// GetName returns the name of the bot. This name is used for creating the
// WebSocket URL and displaying the bot's name within the game.
func (b *RandomBot) GetName() string {
return "RandomBot"
}
20 changes: 20 additions & 0 deletions goClient/internal/model/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package model

// ConnectionRequest represents a request to connect with the gameserver
type ConnectionRequest struct {
Type string `json:"type"`
Name string `json:"name"` // Request type: always "connect"
}

// PlayRequest represents a request to make a move in a specific column.
type PlayRequest struct {
ID int `json:"id"` // Unique identifier of the bot or client
Type string `json:"type"` // Request type: always "play"
Column int `json:"column"` // Column index where the move should be played
}

// StateRequest represents a request to retrieve the current game state.
type StateRequest struct {
ID int `json:"id"` // Unique identifier of the bot or client
Type string `json:"type"` // Request type: always "getState"
}
Loading