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
22 changes: 22 additions & 0 deletions clients/common_lisp_sbcl/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM alpine:latest
VOLUME c4xbot

ARG SBCL_VERSION="2.5.10"
ARG URL="https://phoenixnap.dl.sourceforge.net/project/sbcl/sbcl/2.5.10/sbcl-2.5.10-x86-64-linux-binary.tar.bz2?viasf=1"

RUN apk add --no-cache curl gcc sbcl make libc-dev linux-headers

RUN cd /tmp && curl -O https://phoenixnap.dl.sourceforge.net/project/sbcl/sbcl/2.5.10/sbcl-2.5.10-source.tar.bz2?viasf=1 && tar jxvf sbcl-2.5.10-source.tar.bz2 && cd /tmp/sbcl-2.5.10 && sh ./make.sh && sh ./install.sh && rm -rf /tmp/sbcl* && \
apk del sbcl && \
cd /tmp && curl -O https://beta.quicklisp.org/quicklisp.lisp && echo "(load \"quicklisp.lisp\") (quicklisp-quickstart:install :path \"/opt/quicklisp\") (ql::without-prompting (ql:add-to-init-file))" | sbcl && cp $HOME/.sbclrc /etc/sbclrc && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

WORKDIR /app

ADD . /app

RUN sbcl --eval "(load \"/app/common_lisp_sbcl.asd\")" \
--eval "(ql:quickload :c4x-bot)" \
--eval "(quit)"

ENTRYPOINT ["/app/run.sh"]
14 changes: 14 additions & 0 deletions clients/common_lisp_sbcl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Common Lisp (SBCL) Four Connect Xtreme Client

Das ist eine Client Implementierung in Common Lisp für das Four Connect Xtreme Turnier.

## Entwicklung
Um einen Bot für das Turnier zu entwickeln muss einfach in der bots.lisp Datei die Funktion `defbot` mit einem Namen und einer Funktion aufgerufen werden. Die Funktion bekommt als einzigen Parameter den aktuellen State übergeben. Es finden sich Beispiele wie "Dumpling" und "FirstIsKey" in der `bots.lisp` Datei.

## Deployment
Um den Bot zu starten kann das beigelegte Dockerfile verwendet werden. Dazu müssen die folgenden Commands ausgeführt werden:

1. `docker build -t c4xbot .` - Dieses Command baut den Container zusammen und lädt alle Dependencies herunter.
2. `docker run --add-host host.docker.internal:host-gateway c4xbot <name des bots> <websocket>` - Der `--add-host host.docker.internal:host-gateway` Parameter wird scheinbar nur unter Linux gebraucht, unter Windows und MacOS kann das weggelassen werden. Also beispielsweise: `docker run --add-host host.docker.internal:host-gateway c4xbot DUMPLING host.docker.internal:5051`.
3. Der Bot sollte sich verbinden und die Spiele spielen.
4. Da der Bot derzeit den Main-Thread ausschließlich zum sleepen verwendet kann der Dockerprozess nur mit Ctrl-C Ctrl-C Ctrl-C abgebrochen werden. Das liegt daran dass ich keine Möglichkeit gefunden habe zu prüfen ob noch eine Verbindung existiert oder ob wir schon disconnected sind.
32 changes: 32 additions & 0 deletions clients/common_lisp_sbcl/bots.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(in-package #:c4x-bot)

;; Hier können Bots implementiert werden. Die Bots werden ganz einfach mit der
;; defbot Funktion erzeugt. Die lambda Funktion bekommt einen Parameter übergeben, dass ist der
;; aktuelle Spielstand. Dabei handelt es sich um eine Hash Table mit den Werten:
;; - board
;; - bombs
;; - bot name
;; - coin-id
;; - round number

;; Beispiel für einen Bot der den State dumped und random Moves spielt.
(defbot "Dumpling"
(lambda (state)
(let ((board (gethash "board" state))
(bombs (gethash "bombs" state))
(bot (gethash "bot" state))
(coin-id (gethash "coin_id" state))
(round (gethash "round" state)))
(format *standard-output* "~%Board:~%~A~%" board)
(format *standard-output* "~%Bombs:~%~A~%" bombs)
(format *standard-output* "~%Bot:~%~A~%" bot)
(format *standard-output* "~%CoinId:~%~A~%" coin-id)
(format *standard-output* "~%Round:~%~A~%" round)
(finish-output))
(random 7)))

;; Beispiel für einen Bot der immer in Index 0 wirft
(defbot "FirstIsKey" (lambda (state)
(declare (ignore state))
0))

16 changes: 16 additions & 0 deletions clients/common_lisp_sbcl/common_lisp_sbcl.asd
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
;;;; common_lisp_sbcl.asd

(asdf:defsystem #:c4x-bot
:description "The client for the Connect4 Extreme tournament."
:author "Timo Netzer <ecso@ecsodikas.eu>"
:license "GPL-3.0"
:version "0.0.1"
:serial t
:depends-on (:clack
:websocket-driver-client
:yason)
:components ((:file "package")
(:file "factory")
(:file "bots")
(:file "core")
(:file "main")))
29 changes: 29 additions & 0 deletions clients/common_lisp_sbcl/core.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
(in-package #:c4x-bot)

(defun play-turn (name state)
(when (string= name (gethash "bot" state))
(let ((column (funcall (gethash name *bot-registry*) state))
(hash-table (make-hash-table)))
(setf (gethash "state" hash-table) "play")
(setf (gethash "column" hash-table) column)
(yason:with-output-to-string* ()
(yason:encode hash-table)))))

(defun handle-message (client name message)
(handler-case
(let ((state (yason:parse (babel:octets-to-string message :encoding :utf-8))))
(wsd:send client (play-turn name state)))
(error ()
(format t "~%~&Got PING from server.~%")
(finish-output))))

(defvar *client* nil)

(defun start-client (host name)
(setf *client* (wsd:make-client (format nil "ws://~A/~A" host name)))
(format t "~%Trying to connect to ~A~%" host)
(wsd:start-connection *client*)
(wsd:on :message *client* (lambda (msg) (handle-message *client* name msg))))

(defun end-client ()
(wsd:close-connection *client*))
9 changes: 9 additions & 0 deletions clients/common_lisp_sbcl/factory.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(in-package #:c4x-bot)

;; Hier werden die Evaluierungsfunktionen der Bots die via `defbot` registriert wurden
;; abgespeichert.
(defparameter *bot-registry*
(make-hash-table :test 'equal))

(defun defbot (name get-move)
(setf (gethash (string-upcase name) *bot-registry*) get-move))
14 changes: 14 additions & 0 deletions clients/common_lisp_sbcl/main.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
;;;; main.lisp

(in-package #:c4x-bot)

(defun main ()
(let* ((args sb-ext:*posix-argv*)
(name (string-upcase (nth 1 args)))
(host (nth 2 args)))
(format t "~A" args)
(when (null (gethash name *bot-registry*))
(error (format nil "Bot with name ~A not registered." name)))
(start-client host name)
(loop :for i :from 0
:do (sleep 1))))
4 changes: 4 additions & 0 deletions clients/common_lisp_sbcl/package.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
;;;; package.lisp

(defpackage #:c4x-bot
(:use #:cl))
3 changes: 3 additions & 0 deletions clients/common_lisp_sbcl/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env sh

sbcl --eval "(load \"common_lisp_sbcl.asd\")" --eval "(ql:quickload :c4x-bot)" --eval "(in-package :c4x-bot)" --eval "(main)" "$1" "$2"