Commit 02a55081 authored by ceddral's avatar ceddral
Browse files

game: implement scoring - partial

farm require neighboorhood information between farms and cities,
which is not yet encoded.
end game scoring is not yet implemented
parent fa51bbcc
......@@ -240,6 +240,132 @@ func (b *Board) HasNeighbour(x, y int) (ok bool) {
return false
}
// assign score to majority players if structure is closed or game is ending
// TryScore may be called repeatedly on previously scored structure
func (b *Board) TryScore(x, y int, id byte, gameEnd bool) (closed bool, score int) {
place, ok := b.Get(x, y)
if !ok {
return false, score
}
// evaluate score, closed
closed = true
score = 0
pennants := 0
switch place.piece.fieldIdToType[id] {
case fCloister:
for dx := -1; dx <= 1; dx++ {
for dy := -1; dy <= 1; dy++ {
_, ok := b.Get(x + dx, y + dy)
if ok {
score++
}
}
}
closed = (score == 9)
case fRoad:
visited := make(map[Coord]struct{})
hasInn := false
b.MapStructure(func(acc interface{}, cur MapCursor)interface{} {
c := Coord{cur.x, cur.y}
if _, ok := visited[c]; !ok {
place, _ := b.Get(c.x, c.y) // can never fail, map only visits placed pieces
for _, s := range place.piece.fieldIdToSpecial[cur.id] {
switch s {
case fInn: hasInn = true
}
}
}
visited[c] = struct{}{}
for _, nStructure := range b.GetNeighbourStructures(cur, true) {
if 0 == nStructure.id {
closed = false
}
}
return struct{}{}
}, struct{}{}, x, y, id)
score = len(visited)
if closed && hasInn {
score = 2 * score
} else if !closed && hasInn {
score = 0
}
case fCity:
visited := make(map[Coord]struct{})
hasCathedral := false
b.MapStructure(func(acc interface{}, cur MapCursor)interface{} {
c := Coord{cur.x, cur.y}
if _, ok := visited[c]; !ok {
place, _ := b.Get(c.x, c.y) // can never fail, map only visits placed pieces
for _, s := range place.piece.fieldIdToSpecial[cur.id] {
switch s {
case fPennant: pennants++
case fCathedral: hasCathedral = true
}
}
}
visited[c] = struct{}{}
for _, nStructure := range b.GetNeighbourStructures(cur, true) {
if 0 == nStructure.id {
closed = false
}
}
return struct{}{}
}, struct{}{}, x, y, id)
score = pennants + len(visited)
if closed && hasCathedral {
score = 3 * score
} else if closed && !hasCathedral {
score = 2 * score
} else if !closed && !hasCathedral {
score = 1 * score
} else if !closed && hasCathedral {
score = 0
}
case fFarm:
println("farm scoring not yet implemented")
return false, 0
default:
panic("trying to score unknown structure type")
}
if !closed && !gameEnd {
return
}
// assign score, return followers to players
majority := 0
// map player -> sum of token weights
weights := make(map[*GamePlayer]int)
b.MapStructure(func(acc interface{}, c MapCursor)interface{} {
place, _ := b.GetWritable(c.x, c.y) // can never fail, map only visits placed pieces
p := place.fieldIdToPlayer[c.id]
if p == nil {
return struct{}{}
}
t := place.fieldIdToToken[c.id]
if _, ok := weights[p]; !ok {
weights[p] = 0
}
weights[p] += t.weight(pennants)
if majority < weights[p] {
majority = weights[p]
}
// return followers
delete(place.fieldIdToPlayer, c.id)
delete(place.fieldIdToToken, c.id)
p.tokens[t]++
b.Set(c.x, c.y, place)
return struct{}{}
}, struct{}{}, x, y, id)
if majority > 0 {
for p, w := range weights {
if majority == w {
p.score += score
}
}
}
return
}
var (
checkerBoardBlockA []byte = tg.RenderBlock(tg.ColorDefault, tg.ColorDefault, ' ', ^tg.TermUnderline, ^tg.TermBold)
checkerBoardBlockB []byte = tg.RenderBlock(tg.ColorDefault, tg.ColorDefault, '\'', ^tg.TermUnderline, ^tg.TermBold)
......
......@@ -54,7 +54,7 @@ func (m *GameMenu) Redraw(s GameState) {
if p == a {
brackets = "<>"
}
buf := []byte(fmt.Sprintf("%c%d%c%s %d", brackets[0], p.id, brackets[1], p.name, 1337))
buf := []byte(fmt.Sprintf("%c%d%c%s %d", brackets[0], p.id, brackets[1], p.name, p.score))
// TODO append barrels cloths grain
v.DrawTextClearLine(fg, bg, buf, 0, line)
maxlen := len(buf)
......@@ -95,6 +95,7 @@ type GamePlayer struct {
boardy int
boardMode PieceMode
next *GamePlayer
score int
}
type Token int
......@@ -160,6 +161,16 @@ func (t Token) isAddon() bool {
}
}
func (t Token) weight(pennants int) int {
switch (t) {
case tFollower: return 1
case tBigFollower: return 2
case tMayor: return pennants
case tWagon: return 1
default: return 0
}
}
func tokensByExtension(ext string) map[Token]int {
switch ext {
case "base":
......@@ -286,6 +297,7 @@ func NewGame(pns []string, extensions []string) (g *Game) {
boardx: -10,
boardy: -10,
boardMode: PMPlayers,
score: 0,
}
p.menu = GameMenu{p}
g.players[name] = p
......@@ -624,7 +636,7 @@ func (s *StatePlaceFollower) agentAction(p *GamePlayer, key Key) bool {
t := toToken(byte(key))
switch true {
case keyReturn == key:
s.g.broadcastState(NewStatePlacePiece(s.g, s.agent.next))
s.g.broadcastState(NewStateScore(s.g, s.agent, s.boardx, s.boardy))
return true
case keyEscape == key:
newFieldId = 0
......@@ -739,3 +751,47 @@ func (s *StatePlaceFollower) patientAction(p *GamePlayer, key Key) bool {
p.boardv.DrawTextClearLine(tg.ColorDefault, tg.ColorDefault, []byte(fmt.Sprintf("It is %s turn. PLEASE WAIT!!!!!", s.agent.name)), 0, 1)
return false
}
type StateScore struct{
agent *GamePlayer
g *Game
boardx int
boardy int
}
func NewStateScore(g *Game, a *GamePlayer, x, y int) *StateScore {
return &StateScore{
g: g,
agent: a,
boardx: x,
boardy: y,
}
}
func (s *StateScore) getAgent() *GamePlayer { return s.agent }
func (s *StateScore) getGame() *Game { return s.g }
func (s *StateScore) String() string { return "StateScore" }
func (s *StateScore) agentTransition(p *GamePlayer) {
place, _ := s.g.board.Get(s.boardx, s.boardy)
for id := range place.piece.fieldIdToType {
s.g.board.TryScore(s.boardx, s.boardy, id, false)
}
// score surrounding cloisters
for dx := -1; dx <= 1; dx++ {
for dy := -1; dy <= 1; dy++ {
place, ok := s.g.board.Get(s.boardx + dx, s.boardy + dy)
if !ok {
continue
}
for id, t := range place.piece.fieldIdToType {
if t == fCloister {
s.g.board.TryScore(s.boardx + dx, s.boardy + dy, id, false)
}
}
}
}
s.g.broadcastState(NewStatePlacePiece(s.g, s.agent.next))
}
func (s *StateScore) patientTransition(p *GamePlayer) {}
func (s *StateScore) agentAction(p *GamePlayer, key Key) bool { return false }
func (s *StateScore) patientAction(p *GamePlayer, key Key) bool { return false }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment