Commit 534a1eca authored by ceddral's avatar ceddral
Browse files

game: tokenPlacement: refactor, only show possible placements

parent b5b723fa
......@@ -286,7 +286,15 @@ func (p *Piece) render(v *tg.View, viewx, viewy int, board *Board, boardx, board
}
}
case PMPlaces:
if f.placeable {
if !f.placeable {
break
}
// if this is the current piece we can show the places that can actually be placed on. otherwise show all places
if spf, ok := s.(*StatePlaceFollower); s != nil && ok && spf.boardx == boardx && spf.boardy == boardy {
if len(spf.placements[f.id]) > 0 {
c = f.id
}
} else {
c = f.id
}
case PMTokens:
......
......@@ -94,6 +94,7 @@ type GamePlayer struct {
type Token int
const (
// order matters, lowest value token is chosen as default wherever necessary
tNone Token = iota - 1
tFollower
tBigFollower
......@@ -127,6 +128,32 @@ func toToken(b byte) Token {
return tNone
}
func (t Token) placeable(fType byte) bool {
switch (t) {
case tFollower: return true
case tBigFollower: return true
case tMayor: return fType == 'r' || fType == 't'
case tWagon: return fType != 'b' || fType == 't'
case tBuilder: return fType == 'r' || fType == 'w'
case tPig: return fType == 'b'
case tTower: return fType == 't'
default: panic("unexpected token can not be converted to byte representation")
}
}
func (t Token) isAddon() bool {
switch (t) {
case tFollower: return false
case tBigFollower: return false
case tMayor: return false
case tWagon: return false
case tBuilder: return true
case tPig: return true
case tTower: return true
default: panic("unexpected token can not be converted to byte representation")
}
}
func tokensByExtension(ext string) map[Token]int {
switch ext {
case "base":
......@@ -465,7 +492,7 @@ func (s *StatePlacePiece) agentAction(p *GamePlayer, key Key) bool {
}
}
s.g.board.Set(s.boardx, s.boardy, NewPlace(s.p, s.rot))
newState = &StatePlaceFollower{s.agent, s.g, s.boardx, s.boardy, PMPlaces, 0, tFollower}
newState = NewStatePlaceFollower(s.g, s.agent, s.boardx, s.boardy)
case keyUp, 'w':
s.boardy--
newState = s
......@@ -507,6 +534,20 @@ type StatePlaceFollower struct {
mode PieceMode
followerFieldId byte
follower Token
placements map[byte][]Token
}
func NewStatePlaceFollower(g *Game, a *GamePlayer, x, y int) *StatePlaceFollower {
return &StatePlaceFollower{
agent: a,
g: g,
boardx: x,
boardy: y,
mode: PMPlaces,
followerFieldId: 0,
follower: tFollower,
placements: g.getPossiblePlacements(a, x, y),
}
}
func (s *StatePlaceFollower) getAgent() *GamePlayer { return s.agent }
......@@ -524,18 +565,20 @@ func (s *StatePlaceFollower) patientTransition(p *GamePlayer) {
func (s *StatePlaceFollower) agentAction(p *GamePlayer, key Key) bool {
prevFieldId := s.followerFieldId
prevFollower := s.follower
newFieldId := prevFieldId
newFollower := prevFollower
t := toToken(byte(key))
switch true {
case keyReturn == key:
s.g.broadcastState(NewStatePlacePiece(s.g, s.agent.next))
return true
case keyEscape == key:
s.followerFieldId = 0
s.follower = tFollower
newFieldId = 0
newFollower = tFollower
case '0' <= key && key <= '9':
s.followerFieldId = byte(key)
newFieldId = byte(key)
case tNone != t:
s.follower = t
newFollower = t
default:
return false
}
......@@ -543,73 +586,101 @@ func (s *StatePlaceFollower) agentAction(p *GamePlayer, key Key) bool {
place, _ := s.g.board.GetWritable(s.boardx, s.boardy) // must always be ok, we just placed a piece there
// check placement validity
// field id invalid?
if s.followerFieldId > 0 {
valid := true
if _, ok := place.piece.fieldIdToType[s.followerFieldId]; !ok {
valid = false
} else {
// check if structure is occupied
occupied := s.g.board.MapStructure(func(acc interface{}, c MapCursor)interface{}{
occupied := acc.(bool)
p, _ := s.g.board.Get(c.x, c.y)
if _, ok := p.fieldIdToPlayer[c.id]; ok {
if c.x == s.boardx && c.y == s.boardy && c.id == prevFieldId {
// ignore currently to be placed follower
return occupied
}
return true
}
return occupied
}, false, s.boardx, s.boardy, s.followerFieldId).(bool)
if occupied {
valid = false
if 0 != newFieldId {
// with descending priority pick newFollower, prevFollower, follower with minimal id
minF := tMax
prevF := tMax
newF := tMax
for _, t := range s.placements[newFieldId] {
if t == prevFollower {
prevF = t
}
if t < minF {
minF = t
}
if t == newFollower {
newF = t
break
}
}
if !valid {
// discard placement
s.followerFieldId = prevFieldId
s.follower = prevFollower
return false
if tMax == minF {
return true
}
if tMax != newF {
newFollower = newF
} else if tMax != prevF {
newFollower = prevF
} else if tMax != minF {
newFollower = minF
}
}
// desired follower not available?
if n, ok := p.tokens[s.follower]; !ok || n < 1 {
s.follower = prevFollower
}
// previous(default) not available?
if n, ok := p.tokens[s.follower]; !ok || n < 1 {
// neither desired nor default follower available
// default to no placement
s.followerFieldId = 0
if 0 != prevFieldId {
// remove old placement
delete(place.fieldIdToPlayer, prevFieldId)
delete(place.fieldIdToToken, prevFieldId)
p.tokens[prevFollower]++
}
if 0 == s.followerFieldId {
if 0 == newFieldId {
// no placement, show possible ones
s.mode = PMPlaces
} else {
// placement valid, show token type
s.mode = PMTokens
}
// delete previous placement if any
if 0 != prevFieldId {
delete(place.fieldIdToPlayer, prevFieldId)
delete(place.fieldIdToToken, prevFieldId)
p.tokens[s.follower]++
}
// add new placement in any
if 0 != s.followerFieldId {
place.fieldIdToPlayer[s.followerFieldId] = p
place.fieldIdToToken[s.followerFieldId] = s.follower
p.tokens[s.follower]--
if 0 != newFieldId {
// set new placement
place.fieldIdToPlayer[newFieldId] = p
place.fieldIdToToken[newFieldId] = newFollower
p.tokens[newFollower]--
}
s.followerFieldId = newFieldId
s.follower = newFollower
s.g.board.Set(s.boardx, s.boardy, place)
s.g.broadcastState(s)
return true
}
func (g *Game) getPossiblePlacements(p *GamePlayer, x, y int) (placements map[byte][]Token) {
placements = make(map[byte][]Token)
place, ok := g.board.Get(x, y)
if !ok {
return
}
for fId, fType := range place.piece.fieldIdToType {
occupied := false
selfOccupied := false
g.board.MapStructure(func(acc interface{}, c MapCursor)interface{}{
place, _ := g.board.Get(c.x, c.y)
if otherP, ok := place.fieldIdToPlayer[c.id]; ok {
occupied = true
if otherP == p {
selfOccupied = true
}
}
return struct{}{}
}, struct{}{}, x, y, fId)
ts := make([]Token, 0)
for t, n := range p.tokens {
if n < 1 {
continue
}
if !t.placeable(fType) {
continue
}
if !t.isAddon() && occupied {
continue
}
if t.isAddon() && !selfOccupied {
continue
}
ts = append(ts, t)
}
placements[fId] = ts
}
return
}
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
......
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