update
This commit is contained in:
@@ -30,13 +30,43 @@ static func _own(values: Array, face: int) -> int:
|
||||
return c
|
||||
|
||||
|
||||
## Wahrscheinlichkeit, dass ein Gebot (count, face) genau stimmt.
|
||||
static func exact_chance(values: Array, unknown: int, count: int, face: int) -> float:
|
||||
var k := count - _own(values, face)
|
||||
if k < 0 or k > unknown:
|
||||
return 0.0
|
||||
return _pmf(unknown, k, 1.0 / 6.0)
|
||||
|
||||
|
||||
## Wahrscheinlichkeit, dass ein Gebot (count, face) hält.
|
||||
static func chance(values: Array, unknown: int, count: int, face: int) -> float:
|
||||
return tail(unknown, count - _own(values, face), 1.0 / 6.0)
|
||||
|
||||
|
||||
## 0 = leicht, 1 = normal, 2 = schwer. Das Menü setzt es vor dem Start.
|
||||
static var stufe := 1
|
||||
|
||||
## Je Stufe vier Regler:
|
||||
## bluff - wie oft er blufft, statt ein glaubwürdiges Gebot anzufechten.
|
||||
## Ohne das wäre er ein reiner Rechner, und den liest man.
|
||||
## genau - ab welcher Trefferchance er "Genau" ruft.
|
||||
## zweifel - unter welcher Haltechance er anficht. Hoch heißt: voreilig.
|
||||
## sicher - welche Chance ein eigenes Gebot mindestens haben muss.
|
||||
const STUFEN := [
|
||||
{"bluff": 0.15, "genau": 0.35, "zweifel": 0.40, "sicher": 0.30},
|
||||
{"bluff": 0.40, "genau": 0.22, "zweifel": 0.30, "sicher": 0.42},
|
||||
{"bluff": 0.50, "genau": 0.18, "zweifel": 0.24, "sicher": 0.50},
|
||||
]
|
||||
## Wie oft er ein sicheres Gebot einen Schritt höher setzt, als nötig wäre.
|
||||
const FRECH := 0.12
|
||||
|
||||
|
||||
static func _regler() -> Dictionary:
|
||||
return STUFEN[clampi(stufe, 0, STUFEN.size() - 1)]
|
||||
|
||||
|
||||
static func choose_action(values: Array, total_dice: int, bid: Dictionary,
|
||||
min_raise: int, must_bid: bool, rng: RandomNumberGenerator) -> Dictionary:
|
||||
must_bid: bool, rng: RandomNumberGenerator) -> Dictionary:
|
||||
var known: Array = []
|
||||
for v in values:
|
||||
if v > 0:
|
||||
@@ -47,26 +77,40 @@ static func choose_action(values: Array, total_dice: int, bid: Dictionary,
|
||||
var op := _opening(known, unknown, rng)
|
||||
return {"type": "bid", "count": op.count, "face": op.face}
|
||||
|
||||
var r := _regler()
|
||||
var hold := chance(known, unknown, bid.count, bid.face)
|
||||
var raise_ = _best_raise(known, unknown, total_dice, bid, min_raise)
|
||||
var raise_ = _best_raise(known, unknown, total_dice, bid, float(r.sicher))
|
||||
var low := lowest_raise(bid, total_dice)
|
||||
|
||||
if not must_bid:
|
||||
var doubt := 0.30 + rng.randf_range(-0.07, 0.07)
|
||||
# Liegt die Ansage vermutlich haarscharf, lohnt "Genau" mehr als die
|
||||
# Anfechtung: der Ansager verliert, und man bekommt selbst einen dazu.
|
||||
if exact_chance(known, unknown, bid.count, bid.face) > float(r.genau) and hold < 0.6:
|
||||
return {"type": "exact"}
|
||||
var doubt := float(r.zweifel) + rng.randf_range(-0.07, 0.07)
|
||||
if hold < doubt and (raise_.is_empty() or raise_.p < 0.32):
|
||||
return {"type": "challenge"}
|
||||
if raise_.is_empty():
|
||||
if must_bid:
|
||||
# Notgebot: minimal erhöhen, auch wenn unwahrscheinlich
|
||||
var c: int = int(bid.count) + maxi(1, min_raise)
|
||||
if c <= total_dice:
|
||||
return {"type": "bid", "count": c, "face": bid.face}
|
||||
if min_raise <= 1 and int(bid.face) < 6:
|
||||
return {"type": "bid", "count": int(bid.count), "face": int(bid.face) + 1}
|
||||
# Es gibt kein gültiges Gebot mehr - dann bleibt nur die Anfechtung.
|
||||
# Kein sicheres Gebot mehr. Anfechten wäre ehrlich - aber wer immer
|
||||
# ehrlich ist, wird gelesen. Also ab und zu bluffen, solange es noch
|
||||
# ein gültiges Gebot gibt.
|
||||
if not low.is_empty() and (must_bid or rng.randf() < float(r.bluff)):
|
||||
return {"type": "bid", "count": low.count, "face": low.face}
|
||||
return {"type": "challenge"}
|
||||
if rng.randf() < FRECH and int(raise_.count) + 1 <= total_dice:
|
||||
return {"type": "bid", "count": int(raise_.count) + 1, "face": raise_.face}
|
||||
return {"type": "bid", "count": raise_.count, "face": raise_.face}
|
||||
|
||||
|
||||
## Das kleinste Gebot, das noch über dem liegenden steht ({} = keins mehr).
|
||||
static func lowest_raise(bid: Dictionary, total: int) -> Dictionary:
|
||||
if int(bid.face) < 6:
|
||||
return {"count": int(bid.count), "face": int(bid.face) + 1}
|
||||
if int(bid.count) + 1 <= total:
|
||||
return {"count": int(bid.count) + 1, "face": 1}
|
||||
return {}
|
||||
|
||||
|
||||
static func _opening(known: Array, unknown: int, rng: RandomNumberGenerator) -> Dictionary:
|
||||
var best_face := rng.randi_range(1, 6)
|
||||
var best := -1
|
||||
@@ -79,14 +123,15 @@ static func _opening(known: Array, unknown: int, rng: RandomNumberGenerator) ->
|
||||
return {"count": count, "face": best_face}
|
||||
|
||||
|
||||
static func _best_raise(known: Array, unknown: int, total: int, bid: Dictionary, min_raise: int) -> Dictionary:
|
||||
static func _best_raise(known: Array, unknown: int, total: int, bid: Dictionary,
|
||||
sicher: float) -> Dictionary:
|
||||
var best := {}
|
||||
for face in range(1, 7):
|
||||
for count in range(1, total + 1):
|
||||
if not _is_higher(count, face, bid, min_raise):
|
||||
if not _is_higher(count, face, bid):
|
||||
continue
|
||||
var p := chance(known, unknown, count, face)
|
||||
if p < 0.42:
|
||||
if p < sicher:
|
||||
continue
|
||||
if best.is_empty() or count < best.count or (count == best.count and p > best.p):
|
||||
best = {"count": count, "face": face, "p": p}
|
||||
@@ -94,22 +139,16 @@ static func _best_raise(known: Array, unknown: int, total: int, bid: Dictionary,
|
||||
return best
|
||||
|
||||
|
||||
static func _is_higher(count: int, face: int, bid: Dictionary, min_raise: int) -> bool:
|
||||
if count >= bid.count + min_raise:
|
||||
static func _is_higher(count: int, face: int, bid: Dictionary) -> bool:
|
||||
if count > bid.count:
|
||||
return true
|
||||
if min_raise > 1:
|
||||
return false
|
||||
return count == bid.count and face > bid.face
|
||||
|
||||
|
||||
## Welcher eigene Würfel soll dran glauben? Für Münzen mit Zielwahl.
|
||||
static func target_for(id: String, values: Array) -> int:
|
||||
if values.is_empty():
|
||||
return 0
|
||||
# Der schwächste Würfel: den neu zu werfen oder zur 6 zu machen bringt
|
||||
# am meisten.
|
||||
var idx := 0
|
||||
## Welcher eigene Würfel soll umgedreht werden? Einer, der die Augenzahl
|
||||
## noch nicht zeigt - sonst wäre die Münze verschenkt.
|
||||
static func target_for(values: Array, face: int) -> int:
|
||||
for i in values.size():
|
||||
if values[i] < values[idx]:
|
||||
idx = i
|
||||
return idx
|
||||
if values[i] != face:
|
||||
return i
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user