Kimble -pelipalvelin

Kimble

Kirjoita haluamallasi ohjelmointikielellä ohjelma, joka osaa jutella pelipalvelimen kanssa ja pelaa kimbleä!

Tällä palvelimella pelataan näillä Kimblen säännöillä

  • Pelissä on 1-4 pelaajaa.
  • Jokaisella pelaajalla on 4 nappulaa.
  • Pelin aloittaja arvotaan.
  • Vuorollaan pelaaja heittää noppaa.
  • Nappula liikkuu aina myötäpäivään koko nopan osoittaman määrän.
  • Kotipesästä saa lähteä vain jos heittää numeron kuusi. Tällöin yhden kotipesän nappulan voi siirtää pelaajan aloitusruutuun.
  • Kuutosen jälkeen pelaaja pelaa uuden heittovuoron.
    • Lisätarkennus: Vaikka kuutosta ei voisi liikkua, niin silti pelaaja pelaa uuden heittovuoron.
  • Vuoroaan ei voi jättää pelaamatta, jos on mahdollista liikuttaa jotain nappulaansa.
  • Vuoron jälkeen heittovuoro siirtyy myötäpäivään.
  • Jos pelinappula lopettaa liikkeensä ruutuun, jossa on vastustajan nappula, vastustajan nappula siirtyy takaisin kotipesäänsä.
  • Oman nappulan päälle ei saa lopettaa liikkumistaan.
  • Kun nappula on kiertänyt laudan, se siirtyy pelaajan omalle maalialueelle.
  • Maalissa on neljä ruutua, ja niihin pitää osua tasaluvulla.
  • Peli loppuu, kun joku pelaajista saa neljännen nappulansa maaliin, ja hän on voittaja.

(Tummennukset sen takia, että näistä sääntökohdista löytyy kuulemma muitakin versioita.)

Pelaaminen, eli Tekoälyn kirjoittaminen

  • Sinä olet Pelaaja A.
  • A on aina mukana. Yksin pelaaminen on mahdollista.
  • Muut pelaajat myötäpäivään ovat B, C ja D. Ketkä tahansa heistä voi ottaa mukaan peliin.
  • Voit valita kanssapelaajille jonkin seuraavista 'tekoäly-strategioista':
    • helppo - pelaaja liikuttaa aina viimeisimpänä olevaa nappulaansa
    • vaikea - pelaaja liikuttaa aina pisimmällä olevaa nappulaansa

Pelilauta

Pelilauta

Pelipalvelin kertoo pelitilanteen lähettämällä Sinulle 44 merkin mittaisen merkkijonon, joka kuvaa nappuloiden sijainnin pelilaudalla:

  • Varattu ruutu on A,B,C tai D riippuen kenen nappula siinä on.
  • Tyhjä ruutu on 0.
  • Kotipesän ruuduilla ei ole minkäänlaista numerointia.
  • Varsinainen pelialue on ruudut 0..27.
  • Pelaajan A ensimmäinen ruutu on 0 ja tästä kiertosuunnan mukaan numerot kasvavat.
    • Joten Pelaajan B ensimmäinen ruutu on 7.
    • Ja Pelaajan C ensimmäinen ruutu on 14
    • Ja Pelaajan D ensimmäinen ruutu on 21
    • Pelaajan A viimeinen ruutu ennen maalia on 27
  • Maaliruudut ovat ruudut 28...43.
    • Pelaajan A maaliruudut ovat 28,29,30,31
    • Ja Pelaajan B maaliruudut ovat 32,33,34,35
    • Ja Pelaajan C maaliruudut ovat 36,37,38,39
    • Ja Pelaajan D maaliruudut ovat 40,41,42,43
  • Kotipesässä olevia nappuloita ei merkitä mitenkään

Pelitilanteen viimeiset merkit ovat: vuorossa olevan pelaajan kirjain ja juuri nopan heiton tulos.

Palvelimen portti

Palvelin on käynnissä portissa 65430. Jos pelipalvelin sammuu (tai kaatuu), niin se käynnistyy automaattisesti seuraavaan porttiin, joten kokeile portit välillä: 65430..65439.

API -versio:001

API toimii sekä REST että RESTless -tilassa. Tämä tarkoittaa, että voit pitää yhteyden auki yhden pelierän ajan, tai voit katkaista yhteyden joka vuoron jälkeen. Serveri ei katkaise yhteyttä. Serveri lähettää kaikkien viestien lopussa merkkijonon END omalla rivillään.

Peliä pelataan autopilotilla, eli pelivuorosi pelataan automaattisesti:

  • jos et voi vuorossasi liikuttaa mitään (esim nappuloita ei ole kentällä, etkä saanut kuutosta)
  • jos voit liikuttaa vain yhtä nappulaa (esim Sinulla on tasan yksi nappula kentällä, etkä saanut kuutosta)

Aloittaaksesi pelin lähetä seuraavankaltainen merkkijono:

  • Ensimmäinen merkki on kirjain v
  • Sitten kolmella numerolla APIn versionumero, eli 001 (koska se on tällä hetkellä ainut vaihtoehto)
  • Sitten kolmella numerolla jokaiselle vastustajalle:
    • 0=Ei ole mukana ollenkaan
    • 1=helppo
    • 2=vaikea

Esimerkiksi jos haluat seuraavanlaiset vastustajat: B on helppo, C ei pelaa ollenkaan ja D on vaikea : v001102.

Serveri vastaa 65 merkkiä pitkällä pelitokenilla, jota tarvitset jokaisessa myöhemmässä viestissäsi. Pelitoken alkaa t-kirjaimella ja sen jälkeen on rivinvaihto. Tämän jälkeen autopilotti pelaa peliä niin pitkälle eteenpäin, kunnes Sinun täytyy tehdä päätös. Jokaisesta pelatusta pelivuorosta tulee yhden rivin pelitilanne. Eli saat niin monta riviä kuin peliä on pelattu (vastustajien vuorot sekä autopilotin pelaamat Sinun vuorosi). Peli voi päättyä ennenkuin saat ensimmäistäkään päätöksentekotilannetta!

Pelin loppuessa pelitilanne-rivien jälkeen tulee merkkijono: Voittaja: jonka perässä on A,B,C tai D. Tämän jälkeen pelitokenisi on tuhottu.

Kun Sinulta kysytään päätöksentekoa, lähetä pelitoken ja heti sen perään (ilman välilyöntiä tai muutakaan) sen nappulan järjestysnumero, jota haluat liikuttaa. Kulloinkin etummainen nappula on 0, seuraavat 1,2 ja 3. Nappulat poistuvat kotipesästä järjestyksessä (jotta esim ensimmäisen heitetyn kuutosen kohdalla ei tarvitse kysyä millä lähdet liikkeelle). Maaliin menneet nappulat lasketaan mukaan.

Jos yrität liikuttaa nappulaa, jota ei voi liikuttaa, serveri vastaa pelitilanteella ja Sinun pitää valita uudestaan nappula.

Serveri tuhoaa pelitokenin, jota ei käytetä 120 sekuntiin.

Tässä on toimiva bash+netcat -toteutus, joka katkaisee yhteyden jokaisen vuoron välissä.

#!/bin/bash

response=`echo v001222 | netcat -q 0 kimble-ai.dy.fi 65430`                #Aloittaa uuden pelin. Api versio 001. Kolme vastustajaa, kaikkien pelilogiikka 2
token=$(echo $response | cut -d" " -f 1)                                   #Token talteen (eka rivi)

echo token=$token                                                          #Näyttää tokenin (jos vaikka kiinnostaa nähdä se?)
while [ true ]                                                             #Silmukka jatkuu kunnes keskeytetään
        do
        if [[ $response == *"Voittaja"* ]]                                 #Tarkistetaan onko serverin vastauksessa merkkijono Voittaja
                then
                echo $response | rev | cut -d " " -f2 | rev                #Tulostetaan kuka voitti
                break                                                      #Lopetetaan
                fi

        n=`shuf -i 0-3 -n 1`                                               #Arvotaan jokin nappuloista: 0,1,2,3 (kehittynyt tekoäly, heh)
        response=`echo $token$n | netcat -q 0 kimble-ai.dy.fi 65430`       #Lähetetään token ja heti sen perässä nappulan numero
        done                                                               #  (todennäköisesti nappulaa ei saa edes liikuttaa, jolloin serveri kertoo pelitilanteen uudestaan)
exit 0

Toimiva python3 toteutus, jossa ei ole tekoälyä ollenkaan, vaan ihminen pelaa itse. Tämä myös 'piirtää pelilaudan', joka voi helpottaa hahmottamista. Maalialueet piirretään tilan puutteen vuoksi kehältä ulospäin eikä laudan keskelle.

#!/usr/bin/env python3

import socket


HOST = 'kimble-ai.dy.fi'
PORT = 65430


def hahmota_pelitilanne(pelilauta):
    '''
    Pelaajan A sisääntulo luoteisessa.
    '''
    if len(pelilauta)<46:
        return
    
    noppa=pelilauta[-1]
    vuorossa=pelilauta[-2]
    
    #A:n ja B:n maalit    
    print (        pelilauta[31] + " "*14 +pelilauta[35])
    print (" "   + pelilauta[30] + " "*12 +pelilauta[34])
    print ("  "  + pelilauta[29] + " "*10 +pelilauta[33])
    print ("   " + pelilauta[28] + " "*8  +pelilauta[32])


    #Piirretään nopan heitto laudan keskelle.
    #Tyhjä tila on 6*6. Piirretään siihen vuorossa olevan kirjain+nopan heitto
    laudan_keskus=[" "*6]*6
    laudan_keskus[2]="  "+vuorossa+"   "
    laudan_keskus[3]="   "+noppa+"  "

    
    print ("    " + pelilauta[0:8])
    for i in range(0,6):
        print ("    " + pelilauta[27-i] + laudan_keskus[i] + pelilauta[8+i])

    alarivi=pelilauta[14:22]
    print ("    " + alarivi[::-1])	#takaperin

    #D:n ja C:n maalit
    print ("   " + pelilauta[40] + " "*8  +pelilauta[36])
    print ("  "  + pelilauta[41] + " "*10 +pelilauta[37])
    print (" "   + pelilauta[42] + " "*12 +pelilauta[38])
    print (        pelilauta[43] + " "*14 +pelilauta[39])


#http://code.activestate.com/recipes/408859-socketrecv-three-ways-to-turn-it-into-recvall/
def recv_end(the_socket):
    total_data=[];data=''
    while True:
            data=the_socket.recv(8192).decode('UTF-8') 
            if "END" in data:
                total_data.append(data[:data.find("END")])
                break
            total_data.append(data)
            if len(total_data)>1:
                #check if end_of_data was split
                last_pair=total_data[-2]+total_data[-1]
                if "END" in last_pair:
                    total_data[-2]=last_pair[:last_pair.find("END")]
                    total_data.pop()
                    break
    return ''.join(total_data)


def pelaa(HOST, PORT):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        for i in range(10):
            try:
                s.connect((HOST, PORT))
                print("Server found in port "+str(PORT))
                break
            except OSError:
                PORT=PORT+1
            
        #Ensin pitää lähettää "v" ja versionumero XXX (001), ja sitten vastustajien 'vaikeustaso' 0=ei pelaa, 1=helppo, 2=vaikea
        s.sendall("v001222\n".encode())
        received=recv_end(s)
        
        if "Virhe" in received:
            print("Ei onnistunut pelin aloittaminen: "+received)
            return        
        rows = received.split("\n") #Jaetaan vastaus rivinvaihdosta riveihin

        #Vastauksessa tulee ekalla rivillä pelitoken mikä pitää ottaa talteen ja lähettää kaikissa seuraavissa viesteissä    
        token=rows[0] #token alkaa aina t-kirjaimella ja on 65 merkkiä pitkä
        
        print ("Sain tokenin="+token+"\n")
        rows=rows[1:] #Jätetään eka rivi pois, koska se oli pelitoken
        
        while True:
            #jokaisella rivillä on yksi pelitilanne
            for r in rows:
                #print("DEBUG: rivi='"+r+"'")        #yhdellä rivillä
                if "Voittaja" in r:
                    print("Peli ohi, "+r+" \n")
                    return
                else:
                    #print("rivi='"+r+"'")        #yhdellä rivillä
                    hahmota_pelitilanne(r)        #laudan muodossa                    
                print ("----------------------------")

            valittu_nappi=input(" Mitä liikutat?\n") #jää odottamaan käyttäjän valintaa
            s.sendall((token+valittu_nappi).encode())
            received=recv_end(s)
            rows = received.split("\n")

lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
pelaa(HOST, PORT)

(Kimbleä valmistaa Tactic Games Oy, jolla ei ole mitään tekemistä tämän sivun kanssa)