This commit is contained in:
team3
2026-09-16 12:09:33 +02:00
parent 9410d18ea5
commit 8a188c5d5c
29 changed files with 2016 additions and 564 deletions

View File

@@ -22,31 +22,111 @@ func setup(rand: RandomNumberGenerator) -> void:
greed = rand.randf_range(0.3, 0.8)
func take_turn(p: PlayerState, pool: Pool, rand: RandomNumberGenerator, round_no: int) -> void:
func take_turn(p: PlayerState, pool: Pool, rand: RandomNumberGenerator) -> void:
_do_upgrades(p, pool)
_do_buys(p, pool, rand, round_no)
_do_placements(p, pool, round_no)
_do_buys(p, pool)
_do_placements(p)
_do_upgrades(p, pool)
var keep := int(lerpf(2.0, 18.0, greed))
if p.gold > keep + Data.REROLL_COST and rand.randf() < 0.6:
p.reroll(pool, rand)
_do_buys(p, pool, rand, round_no)
_do_placements(p, pool, round_no)
_do_upgrades(p, pool)
# Wer kurz vor einem Upgrade steht, würfelt gezielt nach Futter.
var hunting := _closest_upgrade_gap(p) <= 3
var rolls := 3 if hunting else 1
for i in rolls:
if p.gold > keep + Data.REROLL_COST and (hunting or rand.randf() < 0.6):
p.reroll(pool, rand)
_do_buys(p, pool)
_do_placements(p)
_do_upgrades(p, pool)
# Level kaufen, wenn das Brett voll ist und Nachwuchs auf der Bank wartet.
while p.gold > keep + Data.XP_BUY_COST and not p.can_place() \
and _pick_bench_for_board(p) >= 0 and p.buy_xp():
_do_placements(p)
_free_bench(p, pool)
## Stellt das Brett gegen den bekannten Gegner um: Tanks vor seine Schützen,
## eigene Schützen weg von seinen Tanks. Ohne Gegner gilt die Standardaufstellung.
func arrange(p: PlayerState, opp: PlayerState) -> void:
if p.board.is_empty():
return
var units: Array = p.board_units()
p.board.clear()
var threat := [] # Spalten (gespiegelt), in denen gegnerische Schützen stehen
var enemy_tanks := [] # Spalten (gespiegelt) gegnerischer Tanks
if opp != null:
for hex in opp.board:
var col: int = Data.BOARD_COLS - 1 - hex.x
if opp.board[hex].role == Data.Role.TANK:
enemy_tanks.append(col)
else:
threat.append(col)
var center := [3, 2, 4, 1, 5, 0, 6]
var tank_cols := threat.duplicate()
for c in center:
if not tank_cols.has(c):
tank_cols.append(c)
# Schützen möglichst weit weg von gegnerischen Tanks.
var far_cols := center.duplicate()
far_cols.sort_custom(func(a, b):
return _min_dist(a, enemy_tanks) > _min_dist(b, enemy_tanks))
units.sort_custom(func(a, b): return a.role == Data.Role.TANK and b.role != Data.Role.TANK)
for u in units:
var rows: Array = [0, 1, 2, 3] if u.role == Data.Role.TANK else [3, 2, 1, 0]
var cols: Array = tank_cols if u.role == Data.Role.TANK else far_cols
var placed := false
for r in rows:
for c in cols:
var h := Vector2i(c, r)
if not p.board.has(h):
p.board[h] = u
placed = true
break
if placed:
break
func _min_dist(col: int, cols: Array) -> int:
if cols.is_empty():
return 0
var best := 99
for c in cols:
best = mini(best, absi(col - c))
return best
## Kleinste Zahl fehlender Futter-Avatare über alle Board-Einheiten.
func _closest_upgrade_gap(p: PlayerState) -> int:
var best := 99
for hex in p.board:
var miss := p.upgrade_missing(hex)
if p.board[hex].upgrade_cost().is_empty():
continue
var total := 0
for e in miss:
total += miss[e]
best = mini(best, total)
return best
## Elemente, die irgendeine Board-Einheit für ihr nächstes Upgrade braucht.
func _needed_elements(p: PlayerState) -> Dictionary:
var need := {}
for hex in p.board:
for e in p.board[hex].upgrade_cost():
need[e] = true
return need
func _wants(entry: ShopEntry, p: PlayerState) -> bool:
if entry.element < 0:
# Neutral wird beim Kauf spezialisiert, also immer brauchbar.
return true
if pref.has(entry.element):
if _needed_elements(p).has(entry.element):
return true
# Elemente, die schon auf dem Board sind, sind immer Futter.
return p.trait_counts().has(entry.element)
return pref.has(entry.element)
func _do_buys(p: PlayerState, pool: Pool, rand: RandomNumberGenerator, round_no: int) -> void:
func _do_buys(p: PlayerState, pool: Pool) -> void:
for i in p.shop.size():
var e: ShopEntry = p.shop[i]
if e.sold:
@@ -60,8 +140,8 @@ func _do_buys(p: PlayerState, pool: Pool, rand: RandomNumberGenerator, round_no:
p.buy(i, pool, _pick_element(p) if e.element < 0 else e.element)
func _do_placements(p: PlayerState, pool: Pool, round_no: int) -> void:
while p.can_place(round_no):
func _do_placements(p: PlayerState) -> void:
while p.can_place():
var idx := _pick_bench_for_board(p)
if idx < 0:
return
@@ -69,7 +149,7 @@ func _do_placements(p: PlayerState, pool: Pool, round_no: int) -> void:
var hex := _pick_hex(p, a.role)
if hex.x < 0:
return
if not p.place(idx, hex, round_no):
if not p.place(idx, hex):
return
@@ -77,6 +157,7 @@ func _do_placements(p: PlayerState, pool: Pool, round_no: int) -> void:
## Alles Weitere bleibt als Futter auf der Bank.
func _pick_bench_for_board(p: PlayerState) -> int:
var counts := p.trait_counts()
var need := _needed_elements(p)
var best := -1
var best_score := -1
for i in p.bench.size():
@@ -84,7 +165,9 @@ func _pick_bench_for_board(p: PlayerState) -> int:
if a == null:
continue
var score := 0
if pref.has(a.elements[0]):
if need.has(a.elements[0]) and counts.get(a.elements[0], 0) > 0:
score = 0 # Futter für ein laufendes Upgrade bleibt auf der Bank
elif pref.has(a.elements[0]):
score = 3 if counts.get(a.elements[0], 0) == 0 else 1
else:
score = 2 if counts.get(a.elements[0], 0) == 0 else 0
@@ -144,12 +227,16 @@ func _do_upgrades(p: PlayerState, pool: Pool) -> void:
func _free_bench(p: PlayerState, pool: Pool) -> void:
if p.free_bench_slot() >= 0:
return
var counts := p.trait_counts()
var need := _needed_elements(p)
# Erst verkaufen, was weder Futter noch Lieblingselement ist.
for i in p.bench.size():
var a = p.bench[i]
if a == null:
continue
if counts.get(a.elements[0], 0) == 0 and not pref.has(a.elements[0]):
if a != null and not need.has(a.elements[0]) and not pref.has(a.elements[0]):
p.sell_bench(i, pool)
return
for i in p.bench.size():
var a = p.bench[i]
if a != null and not need.has(a.elements[0]):
p.sell_bench(i, pool)
return
p.sell_bench(0, pool)