mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
+30
-1
@@ -1,12 +1,41 @@
|
|||||||
version = "3.9.9"
|
version = "3.9.9"
|
||||||
runner.dialect = scala3
|
runner.dialect = scala3
|
||||||
rewrite.scala3.convertToNewSyntax = true
|
rewrite.scala3.convertToNewSyntax = true
|
||||||
|
# Keep braces, don't use significant indentation
|
||||||
# rewrite.scala3.removeOptionalBraces = yes
|
# rewrite.scala3.removeOptionalBraces = yes
|
||||||
rewrite.scala3.insertEndMarkerMinLines = 15
|
rewrite.scala3.insertEndMarkerMinLines = 15
|
||||||
rewrite.scala3.removeEndMarkerMaxLines = 14
|
rewrite.scala3.removeEndMarkerMaxLines = 14
|
||||||
|
|
||||||
|
# Strip margin settings
|
||||||
|
assumeStandardLibraryStripMargin = false
|
||||||
|
align.stripMargin = true
|
||||||
|
|
||||||
|
# Code Style & Formatting
|
||||||
|
align.preset = more
|
||||||
|
align.multiline = true
|
||||||
|
align.arrowEnumeratorGenerator = true
|
||||||
|
spaces.inImportCurlyBraces = false
|
||||||
|
spaces.beforeContextBoundColon = Never
|
||||||
|
maxColumn = 120
|
||||||
|
docstrings.style = Asterisk
|
||||||
|
docstrings.wrap = yes
|
||||||
|
|
||||||
|
# Method chaining
|
||||||
|
newlines.beforeCurlyLambdaParams = multilineWithCaseOnly
|
||||||
|
optIn.breakChainOnFirstMethodDot = true
|
||||||
|
includeCurlyBraceInSelectChains = false
|
||||||
|
|
||||||
|
# Advanced Scala 3 Features
|
||||||
|
rewrite.scala3.countEndMarkerLines = all
|
||||||
|
rewrite.redundantBraces.stringInterpolation = true
|
||||||
|
rewrite.redundantBraces.parensForOneLineApply = true
|
||||||
|
|
||||||
|
# Project-Specific Considerations
|
||||||
|
optIn.annotationNewlines = true
|
||||||
|
runner.optimizer.forceConfigStyleMinArgCount = 3
|
||||||
|
|
||||||
# Import sorting configuration
|
# Import sorting configuration
|
||||||
rewrite.rules = [SortImports]
|
rewrite.rules = [SortImports, RedundantBraces, RedundantParens]
|
||||||
rewrite.imports.sort = scalastyle
|
rewrite.imports.sort = scalastyle
|
||||||
rewrite.imports.groups = [
|
rewrite.imports.groups = [
|
||||||
["java\\..*"],
|
["java\\..*"],
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ case class RandomState[+T](
|
|||||||
newValue: T,
|
newValue: T,
|
||||||
nextRandom: FunctionalRandom
|
nextRandom: FunctionalRandom
|
||||||
) {
|
) {
|
||||||
def map[U](f: T => U): RandomState[U] = RandomState(f(newValue), nextRandom)
|
def map[U](f: T => U): RandomState[U] = RandomState(f(newValue), nextRandom)
|
||||||
def continue[U](f: (T, FunctionalRandom) => RandomState[U]): RandomState[U] =
|
def continue[U](f: (T, FunctionalRandom) => RandomState[U]): RandomState[U] =
|
||||||
f(newValue, nextRandom)
|
f(newValue, nextRandom)
|
||||||
|
|
||||||
@@ -62,10 +62,9 @@ abstract class FunctionalRandom {
|
|||||||
else ni.nextRandom.nextInt(atLeast = atLeast, lessThan = lessThan)
|
else ni.nextRandom.nextInt(atLeast = atLeast, lessThan = lessThan)
|
||||||
} else {
|
} else {
|
||||||
val diff = lessThan - atLeast
|
val diff = lessThan - atLeast
|
||||||
val max = (Int.MaxValue / diff) * diff
|
val max = (Int.MaxValue / diff) * diff
|
||||||
val np = nextPositiveInt
|
val np = nextPositiveInt
|
||||||
if np.newValue > max then
|
if np.newValue > max then np.nextRandom.nextInt(atLeast = atLeast, lessThan = lessThan)
|
||||||
np.nextRandom.nextInt(atLeast = atLeast, lessThan = lessThan)
|
|
||||||
else RandomState(np.newValue % diff + atLeast, np.nextRandom)
|
else RandomState(np.newValue % diff + atLeast, np.nextRandom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +105,7 @@ abstract class FunctionalRandom {
|
|||||||
nextItems[Double](count, _.nextDouble)
|
nextItems[Double](count, _.nextDouble)
|
||||||
|
|
||||||
def nextPositiveInt: RandomState[Int] = {
|
def nextPositiveInt: RandomState[Int] = {
|
||||||
val ni = nextInt
|
val ni = nextInt
|
||||||
val nonNegative =
|
val nonNegative =
|
||||||
if ni.newValue < 0 then -(ni.newValue + 1)
|
if ni.newValue < 0 then -(ni.newValue + 1)
|
||||||
else ni.newValue
|
else ni.newValue
|
||||||
@@ -127,12 +126,11 @@ abstract class FunctionalRandom {
|
|||||||
shuffledHead: Vector[T],
|
shuffledHead: Vector[T],
|
||||||
unshuffledTail: Vector[T],
|
unshuffledTail: Vector[T],
|
||||||
fr: FunctionalRandom
|
fr: FunctionalRandom
|
||||||
): RandomState[Vector[T]] = {
|
): RandomState[Vector[T]] =
|
||||||
if unshuffledTail.isEmpty then RandomState(shuffledHead, fr)
|
if unshuffledTail.isEmpty then RandomState(shuffledHead, fr)
|
||||||
else if unshuffledTail.length == 1 then
|
else if unshuffledTail.length == 1 then RandomState(shuffledHead ++ unshuffledTail, fr)
|
||||||
RandomState(shuffledHead ++ unshuffledTail, fr)
|
|
||||||
else {
|
else {
|
||||||
val ni = fr.nextInt(0, unshuffledTail.length)
|
val ni = fr.nextInt(0, unshuffledTail.length)
|
||||||
val index = ni.newValue
|
val index = ni.newValue
|
||||||
|
|
||||||
if index == 0 then {
|
if index == 0 then {
|
||||||
@@ -143,20 +141,19 @@ abstract class FunctionalRandom {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val nextHead = shuffledHead :+ unshuffledTail(index)
|
val nextHead = shuffledHead :+ unshuffledTail(index)
|
||||||
val newTail = unshuffledTail.splitAt(index) match {
|
val newTail = unshuffledTail.splitAt(index) match {
|
||||||
case (l, r) =>
|
case (l, r) =>
|
||||||
(l.drop(1) :+ unshuffledTail.head) ++ r.drop(1)
|
(l.drop(1) :+ unshuffledTail.head) ++ r.drop(1)
|
||||||
}
|
}
|
||||||
go(nextHead, newTail, ni.nextRandom)
|
go(nextHead, newTail, ni.nextRandom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
go(Vector(), seq, this)
|
go(Vector(), seq, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
def nextRandomSubset[T](size: Int, seq: Vector[T]): RandomState[Vector[T]] =
|
def nextRandomSubset[T](size: Int, seq: Vector[T]): RandomState[Vector[T]] =
|
||||||
nextShuffled(seq).map { _.take(size) }
|
nextShuffled(seq).map(_.take(size))
|
||||||
|
|
||||||
private def nextFlatMapImpl[T, U, S[X] <: Seq[X], FACT <: SeqFactory[S]](
|
private def nextFlatMapImpl[T, U, S[X] <: Seq[X], FACT <: SeqFactory[S]](
|
||||||
seq: S[T]
|
seq: S[T]
|
||||||
@@ -164,8 +161,9 @@ abstract class FunctionalRandom {
|
|||||||
f: (T, FunctionalRandom) => RandomState[S[U]]
|
f: (T, FunctionalRandom) => RandomState[S[U]]
|
||||||
)(using xFactory: FACT): RandomState[S[U]] = {
|
)(using xFactory: FACT): RandomState[S[U]] = {
|
||||||
@nowarn def foldGen(rsU: RandomState[S[U]], t: T): RandomState[S[U]] =
|
@nowarn def foldGen(rsU: RandomState[S[U]], t: T): RandomState[S[U]] =
|
||||||
rsU.continue { case (us: S[U], fr: FunctionalRandom) =>
|
rsU.continue {
|
||||||
f(t, fr).map(x => (us ++ x).asInstanceOf[S[U]])
|
case (us: S[U], fr: FunctionalRandom) =>
|
||||||
|
f(t, fr).map(x => (us ++ x).asInstanceOf[S[U]])
|
||||||
}
|
}
|
||||||
FunctionalRandom.nextFoldLeft(
|
FunctionalRandom.nextFoldLeft(
|
||||||
seq,
|
seq,
|
||||||
@@ -189,8 +187,9 @@ abstract class FunctionalRandom {
|
|||||||
rsU: RandomState[S[U]],
|
rsU: RandomState[S[U]],
|
||||||
t: T
|
t: T
|
||||||
): RandomState[S[U]] =
|
): RandomState[S[U]] =
|
||||||
rsU.continue { case (us: S[U], fr: FunctionalRandom) =>
|
rsU.continue {
|
||||||
f(t, fr).map(x => (us :+ x).asInstanceOf[S[U]])
|
case (us: S[U], fr: FunctionalRandom) =>
|
||||||
|
f(t, fr).map(x => (us :+ x).asInstanceOf[S[U]])
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionalRandom.nextFoldLeft(seq, RandomState(xFactory.empty[U], this))(
|
FunctionalRandom.nextFoldLeft(seq, RandomState(xFactory.empty[U], this))(
|
||||||
@@ -213,11 +212,10 @@ abstract class FunctionalRandom {
|
|||||||
): RandomState[Vector[U]] = remVec match {
|
): RandomState[Vector[U]] = remVec match {
|
||||||
case head +: tail =>
|
case head +: tail =>
|
||||||
val newAccRS =
|
val newAccRS =
|
||||||
if pf.isDefinedAt(head) then
|
if pf.isDefinedAt(head) then pf(head)(accRS.nextRandom).map(accRS.newValue :+ _)
|
||||||
pf(head)(accRS.nextRandom).map(accRS.newValue :+ _)
|
|
||||||
else accRS
|
else accRS
|
||||||
go(tail, newAccRS)
|
go(tail, newAccRS)
|
||||||
case _ => accRS
|
case _ => accRS
|
||||||
}
|
}
|
||||||
|
|
||||||
go(vec, RandomState(Vector(), this))
|
go(vec, RandomState(Vector(), this))
|
||||||
@@ -232,7 +230,7 @@ abstract class FunctionalRandom {
|
|||||||
case head +: tail =>
|
case head +: tail =>
|
||||||
if pf.isDefinedAt(head) then pf(head)(this).map(Option(_))
|
if pf.isDefinedAt(head) then pf(head)(this).map(Option(_))
|
||||||
else go(tail)
|
else go(tail)
|
||||||
case _ => ??? // above cases should cover
|
case _ => ??? // above cases should cover
|
||||||
}
|
}
|
||||||
|
|
||||||
go(seq)
|
go(seq)
|
||||||
@@ -249,11 +247,10 @@ abstract class FunctionalRandom {
|
|||||||
case items if items.isEmpty => accRS
|
case items if items.isEmpty => accRS
|
||||||
case head +: tail =>
|
case head +: tail =>
|
||||||
val newAccRS =
|
val newAccRS =
|
||||||
if pf.isDefinedAt(head) then
|
if pf.isDefinedAt(head) then pf(head)(accRS.nextRandom).map(accRS.newValue ++ _)
|
||||||
pf(head)(accRS.nextRandom).map(accRS.newValue ++ _)
|
|
||||||
else accRS
|
else accRS
|
||||||
go(tail, newAccRS)
|
go(tail, newAccRS)
|
||||||
case _ => ??? // above cases should cover
|
case _ => ??? // above cases should cover
|
||||||
}
|
}
|
||||||
|
|
||||||
go(vec, RandomState(Vector(), this))
|
go(vec, RandomState(Vector(), this))
|
||||||
@@ -281,13 +278,13 @@ abstract class FunctionalRandom {
|
|||||||
|
|
||||||
case class SeededRandom(override val seed: Long) extends FunctionalRandom {
|
case class SeededRandom(override val seed: Long) extends FunctionalRandom {
|
||||||
private val magicMultiplier = 0x5deece66dL
|
private val magicMultiplier = 0x5deece66dL
|
||||||
private val magicAdder = 0xbL
|
private val magicAdder = 0xbL
|
||||||
private val mask = 0xffffffffffffffffL
|
private val mask = 0xffffffffffffffffL
|
||||||
|
|
||||||
override def nextInt: RandomState[Int] = {
|
override def nextInt: RandomState[Int] = {
|
||||||
// borrowed from Functional Programming in Scala
|
// borrowed from Functional Programming in Scala
|
||||||
val newSeed = (seed * magicMultiplier + magicAdder) & mask
|
val newSeed = (seed * magicMultiplier + magicAdder) & mask
|
||||||
val next = SeededRandom(newSeed)
|
val next = SeededRandom(newSeed)
|
||||||
val newValue = (newSeed >>> 16).toInt
|
val newValue = (newSeed >>> 16).toInt
|
||||||
RandomState(newValue, next)
|
RandomState(newValue, next)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,35 +12,31 @@ final case class GrpcRetrier[Stub <: AbstractStub[Stub]](grpcClient: Stub) {
|
|||||||
ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4))
|
ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4))
|
||||||
|
|
||||||
private val firstDelayMillis = TimeUnit.SECONDS.toMillis(1)
|
private val firstDelayMillis = TimeUnit.SECONDS.toMillis(1)
|
||||||
private val maxDelayMillis = TimeUnit.SECONDS.toMillis(10)
|
private val maxDelayMillis = TimeUnit.SECONDS.toMillis(10)
|
||||||
private val timeoutSeconds = 1200
|
private val timeoutSeconds = 1200
|
||||||
|
|
||||||
private def go[Response](
|
private def go[Response](
|
||||||
clientOp: Stub => Future[Response],
|
clientOp: Stub => Future[Response],
|
||||||
delayMillis: Long
|
delayMillis: Long
|
||||||
): Future[Response] = {
|
): Future[Response] =
|
||||||
clientOp(grpcClient.withDeadlineAfter(timeoutSeconds, TimeUnit.SECONDS))
|
clientOp(grpcClient.withDeadlineAfter(timeoutSeconds, TimeUnit.SECONDS)).recoverWith {
|
||||||
.recoverWith {
|
case sre: StatusRuntimeException if sre.getStatus.getCode == Status.Code.UNAVAILABLE =>
|
||||||
case sre: StatusRuntimeException
|
println(
|
||||||
if sre.getStatus.getCode == Status.Code.UNAVAILABLE =>
|
s"Unavailable, retrying in ${TimeUnit.MILLISECONDS.toSeconds(delayMillis)}s..."
|
||||||
println(
|
)
|
||||||
s"Unavailable, retrying in ${TimeUnit.MILLISECONDS.toSeconds(delayMillis)}s..."
|
Thread.sleep(delayMillis)
|
||||||
)
|
go(
|
||||||
Thread.sleep(delayMillis)
|
clientOp = clientOp,
|
||||||
go(
|
delayMillis = Math.min(delayMillis * 2, maxDelayMillis)
|
||||||
clientOp = clientOp,
|
)
|
||||||
delayMillis = Math.min(delayMillis * 2, maxDelayMillis)
|
case sre: StatusRuntimeException if sre.getStatus.getCode == Status.Code.CANCELLED =>
|
||||||
)
|
println("Cancelled, reconnecting...")
|
||||||
case sre: StatusRuntimeException
|
go(
|
||||||
if sre.getStatus.getCode == Status.Code.CANCELLED =>
|
clientOp = clientOp,
|
||||||
println("Cancelled, reconnecting...")
|
delayMillis = firstDelayMillis
|
||||||
go(
|
)
|
||||||
clientOp = clientOp,
|
case fr: Throwable => throw fr
|
||||||
delayMillis = firstDelayMillis
|
}
|
||||||
)
|
|
||||||
case fr: Throwable => throw fr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def withRetries[Response](
|
def withRetries[Response](
|
||||||
clientOp: Stub => Future[Response]
|
clientOp: Stub => Future[Response]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import org.json4s.DefaultFormats
|
|||||||
|
|
||||||
object JsonUtils {
|
object JsonUtils {
|
||||||
implicit val jsonFormats: DefaultFormats.type = DefaultFormats
|
implicit val jsonFormats: DefaultFormats.type = DefaultFormats
|
||||||
private val json = new Json(jsonFormats)
|
private val json = new Json(jsonFormats)
|
||||||
|
|
||||||
// This will just crash if the source url is not available, or if the
|
// This will just crash if the source url is not available, or if the
|
||||||
// json is not a String:Vector[String] map
|
// json is not a String:Vector[String] map
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package net.eagle0.common
|
|||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
case class Metrics[T](logFrequency: Int) {
|
case class Metrics[T](logFrequency: Int) {
|
||||||
private var counters = mutable.Map[T, Int]()
|
private var counters = mutable.Map[T, Int]()
|
||||||
private var totalCount = 0
|
private var totalCount = 0
|
||||||
private var lastLog = 0
|
private var lastLog = 0
|
||||||
|
|
||||||
def increment(counter: T): Unit =
|
def increment(counter: T): Unit =
|
||||||
this.synchronized {
|
this.synchronized {
|
||||||
@@ -19,15 +19,15 @@ case class Metrics[T](logFrequency: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def logCounters: Unit = this.synchronized {
|
def logCounters: Unit = this.synchronized {
|
||||||
counters.toVector.sortBy(-_._2).foreach { case (t, count) =>
|
counters.toVector.sortBy(-_._2).foreach {
|
||||||
println(s"$count $t")
|
case (t, count) =>
|
||||||
|
println(s"$count $t")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def maybeLogCounters: Unit = {
|
def maybeLogCounters: Unit =
|
||||||
if totalCount - lastLog > logFrequency then {
|
if totalCount - lastLog > logFrequency then {
|
||||||
logCounters
|
logCounters
|
||||||
lastLog = totalCount
|
lastLog = totalCount
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ object MoreSeq {
|
|||||||
|
|
||||||
def irregularTranspose[A](coll: Iterable[Seq[A]]): Vector[Vector[A]] = {
|
def irregularTranspose[A](coll: Iterable[Seq[A]]): Vector[Vector[A]] = {
|
||||||
val maxLength = coll.map(_.length).max
|
val maxLength = coll.map(_.length).max
|
||||||
coll.foldLeft(Vector.fill(maxLength)(Vector[A]())) { case (acc, nextLine) =>
|
coll.foldLeft(Vector.fill(maxLength)(Vector[A]())) {
|
||||||
acc.zipWithIndex.map { case (vec, idx) =>
|
case (acc, nextLine) =>
|
||||||
if idx < nextLine.length then vec :+ nextLine(idx)
|
acc.zipWithIndex.map {
|
||||||
else vec
|
case (vec, idx) =>
|
||||||
}
|
if idx < nextLine.length then vec :+ nextLine(idx)
|
||||||
|
else vec
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ object SimpleTimedLogger {
|
|||||||
private def format(string: String): String =
|
private def format(string: String): String =
|
||||||
s"${formatter.format(Instant.now())} $string"
|
s"${formatter.format(Instant.now())} $string"
|
||||||
|
|
||||||
private def formatLine(string: String): String = s"${format(string)}\n"
|
private def formatLine(string: String): String = s"${format(string)}\n"
|
||||||
private def formatNoLine(string: String): String = s"${format(string)} "
|
private def formatNoLine(string: String): String = s"${format(string)} "
|
||||||
|
|
||||||
private class PrintLogger extends SimpleTimedLoggerT {
|
private class PrintLogger extends SimpleTimedLoggerT {
|
||||||
@@ -44,15 +44,14 @@ object SimpleTimedLogger {
|
|||||||
override def logLine(str: String): Unit = print(formatLine(str))
|
override def logLine(str: String): Unit = print(formatLine(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SimpleTimedLogger(osConstructor: () => OutputStream)
|
private class SimpleTimedLogger(osConstructor: () => OutputStream) extends SimpleTimedLoggerT {
|
||||||
extends SimpleTimedLoggerT {
|
|
||||||
def log(str: String): Unit = write(formatNoLine(str))
|
def log(str: String): Unit = write(formatNoLine(str))
|
||||||
|
|
||||||
def logLine(str: String): Unit = write(formatLine(str))
|
def logLine(str: String): Unit = write(formatLine(str))
|
||||||
|
|
||||||
private def write(str: String): Unit =
|
private def write(str: String): Unit =
|
||||||
Using(new OutputStreamWriter(osConstructor(), StandardCharsets.UTF_8)) {
|
Using(new OutputStreamWriter(osConstructor(), StandardCharsets.UTF_8)) { fw =>
|
||||||
fw => fw.write(str)
|
fw.write(str)
|
||||||
} match {
|
} match {
|
||||||
case Success(_) => ()
|
case Success(_) => ()
|
||||||
case Failure(exception) =>
|
case Failure(exception) =>
|
||||||
@@ -69,7 +68,7 @@ trait SimpleTimedLoggerT {
|
|||||||
def logLine(str: String): Unit
|
def logLine(str: String): Unit
|
||||||
|
|
||||||
def withStopwatch[T](str: String)(f: => T): T = {
|
def withStopwatch[T](str: String)(f: => T): T = {
|
||||||
val start = System.currentTimeMillis()
|
val start = System.currentTimeMillis()
|
||||||
val result = f
|
val result = f
|
||||||
logLine(s"Finished $str in ${System.currentTimeMillis() - start} ms")
|
logLine(s"Finished $str in ${System.currentTimeMillis() - start} ms")
|
||||||
result
|
result
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ object TsvUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def loadLines(url: URL): Try[Vector[Vector[String]]] =
|
def loadLines(url: URL): Try[Vector[Vector[String]]] =
|
||||||
Using(Source.fromURL(url, "UTF-8")) { loadLines }
|
Using(Source.fromURL(url, "UTF-8"))(loadLines)
|
||||||
|
|
||||||
// Loads a tsv and returns a Vector of arrays of strings.
|
// Loads a tsv and returns a Vector of arrays of strings.
|
||||||
def loadLines(path: String): Try[Vector[Vector[String]]] =
|
def loadLines(path: String): Try[Vector[Vector[String]]] =
|
||||||
Using(Source.fromFile(path, "UTF-8")) { loadLines }
|
Using(Source.fromFile(path, "UTF-8"))(loadLines)
|
||||||
|
|
||||||
def loadMaps(url: URL): Try[Vector[ListMap[String, String]]] =
|
def loadMaps(url: URL): Try[Vector[ListMap[String, String]]] =
|
||||||
loadLines(url).map(linesToMaps)
|
loadLines(url).map(linesToMaps)
|
||||||
@@ -80,7 +80,7 @@ object TsvUtils {
|
|||||||
private def mapsToLines(
|
private def mapsToLines(
|
||||||
maps: Vector[Map[String, String]]
|
maps: Vector[Map[String, String]]
|
||||||
): Vector[Vector[String]] = {
|
): Vector[Vector[String]] = {
|
||||||
val headers = maps.head.keys.toVector
|
val headers = maps.head.keys.toVector
|
||||||
val otherLines = maps.map(oneMap => headers.map(oneMap))
|
val otherLines = maps.map(oneMap => headers.map(oneMap))
|
||||||
|
|
||||||
headers +: otherLines
|
headers +: otherLines
|
||||||
@@ -88,12 +88,11 @@ object TsvUtils {
|
|||||||
|
|
||||||
private def saveLines(lines: Vector[Vector[String]], url: URL): Unit = {
|
private def saveLines(lines: Vector[Vector[String]], url: URL): Unit = {
|
||||||
val file = new File(url.getPath)
|
val file = new File(url.getPath)
|
||||||
val pw = new PrintWriter(file)
|
val pw = new PrintWriter(file)
|
||||||
|
|
||||||
try {
|
try
|
||||||
lines.foreach(line => pw.write(line.mkString("\t") + "\n"))
|
lines.foreach(line => pw.write(line.mkString("\t") + "\n"))
|
||||||
} finally {
|
finally
|
||||||
pw.close()
|
pw.close()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ object ZipUtils {
|
|||||||
val os = new ByteArrayOutputStream()
|
val os = new ByteArrayOutputStream()
|
||||||
|
|
||||||
val buf = new Array[Byte](1024)
|
val buf = new Array[Byte](1024)
|
||||||
var n = gz.read(buf)
|
var n = gz.read(buf)
|
||||||
while n > 0 do {
|
while n > 0 do {
|
||||||
os.write(buf, 0, n)
|
os.write(buf, 0, n)
|
||||||
n = gz.read(buf)
|
n = gz.read(buf)
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ object ApiKeys {
|
|||||||
"src/main/scala/net/eagle0/common/llm_integration/api_keys.txt"
|
"src/main/scala/net/eagle0/common/llm_integration/api_keys.txt"
|
||||||
|
|
||||||
// These must have valid values set in api_keys.txt
|
// These must have valid values set in api_keys.txt
|
||||||
private val openAIKeyName = "openai_api_key"
|
private val openAIKeyName = "openai_api_key"
|
||||||
private val anthropicKeyName = "anthropic_api_key"
|
private val anthropicKeyName = "anthropic_api_key"
|
||||||
|
|
||||||
private def readDictionary(): Map[String, String] = {
|
private def readDictionary(): Map[String, String] =
|
||||||
Using(scala.io.Source.fromFile(apiKeyFilePath)) { source =>
|
Using(scala.io.Source.fromFile(apiKeyFilePath)) { source =>
|
||||||
val lines = source.getLines().filterNot(_.startsWith("#")).toList
|
val lines = source.getLines().filterNot(_.startsWith("#")).toList
|
||||||
lines.map { line =>
|
lines.map { line =>
|
||||||
@@ -27,21 +27,19 @@ object ApiKeys {
|
|||||||
"Failed to read api_keys.txt. Make sure you have copied api_keys_template.txt to api_keys.txt and set the keys."
|
"Failed to read api_keys.txt. Make sure you have copied api_keys_template.txt to api_keys.txt and set the keys."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private val dictionary = readDictionary()
|
private val dictionary = readDictionary()
|
||||||
|
|
||||||
private def getKey(key: String): String = {
|
private def getKey(key: String): String =
|
||||||
dictionary.get(key) match {
|
dictionary.get(key) match {
|
||||||
case None =>
|
case None =>
|
||||||
throw new ApiKeyException(s"$key not found")
|
throw new ApiKeyException(s"$key not found")
|
||||||
case Some(value) if value.startsWith("your_") =>
|
case Some(value) if value.startsWith("your_") =>
|
||||||
throw new ApiKeyException(
|
throw new ApiKeyException(
|
||||||
s"API key for $key is not set. Please set it in $apiKeyFilePath."
|
s"API key for $key is not set. Please set it in $apiKeyFilePath."
|
||||||
)
|
)
|
||||||
case Some(value) => value
|
case Some(value) => value
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
lazy val openAI: String = getKey(openAIKeyName)
|
lazy val openAI: String = getKey(openAIKeyName)
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ import java.time.{Duration, ZonedDateTime}
|
|||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
import net.eagle0.common.llm_integration.ClaudeServiceImpl.{
|
import net.eagle0.common.llm_integration.ClaudeServiceImpl.{dictionaryFor, messageVector}
|
||||||
dictionaryFor,
|
|
||||||
messageVector
|
|
||||||
}
|
|
||||||
import org.json4s.{DefaultFormats, JString}
|
import org.json4s.{DefaultFormats, JString}
|
||||||
import org.json4s.jvalue2extractable
|
import org.json4s.jvalue2extractable
|
||||||
import org.json4s.jvalue2monadic
|
import org.json4s.jvalue2monadic
|
||||||
@@ -18,22 +15,22 @@ import org.json4s.native.{Json, Serialization}
|
|||||||
|
|
||||||
object ClaudeServiceImpl {
|
object ClaudeServiceImpl {
|
||||||
val model: String = "claude-sonnet-4-20250514"
|
val model: String = "claude-sonnet-4-20250514"
|
||||||
val maxTokens = 2048
|
val maxTokens = 2048
|
||||||
|
|
||||||
private val apiKey = ApiKeys.anthropic
|
private val apiKey = ApiKeys.anthropic
|
||||||
private val baseURL = new URL("https://api.anthropic.com/v1/messages")
|
private val baseURL = new URL("https://api.anthropic.com/v1/messages")
|
||||||
private def dictionaryFor(
|
private def dictionaryFor(
|
||||||
modelName: String
|
modelName: String
|
||||||
): Map[String, Any] = Map(
|
): Map[String, Any] = Map(
|
||||||
"model" -> modelName,
|
"model" -> modelName,
|
||||||
"max_tokens" -> maxTokens,
|
"max_tokens" -> maxTokens,
|
||||||
"stream" -> true
|
"stream" -> true
|
||||||
)
|
)
|
||||||
|
|
||||||
private def messageVector(inputText: String): Vector[Map[String, String]] =
|
private def messageVector(inputText: String): Vector[Map[String, String]] =
|
||||||
Vector(
|
Vector(
|
||||||
Map(
|
Map(
|
||||||
"role" -> "user",
|
"role" -> "user",
|
||||||
"content" -> inputText
|
"content" -> inputText
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -83,14 +80,14 @@ class ClaudeServiceImpl(
|
|||||||
private class ClaudeStringConsumer(
|
private class ClaudeStringConsumer(
|
||||||
streamingConsumer: Consumer[StreamingTextResults]
|
streamingConsumer: Consumer[StreamingTextResults]
|
||||||
) extends Consumer[String] {
|
) extends Consumer[String] {
|
||||||
private val json = new Json(DefaultFormats)
|
private val json = new Json(DefaultFormats)
|
||||||
private var streamId = ""
|
private var streamId = ""
|
||||||
|
|
||||||
override def accept(t: String): Unit = {
|
override def accept(t: String): Unit = {
|
||||||
val parsedJson = json.parse(t)
|
val parsedJson = json.parse(t)
|
||||||
|
|
||||||
parsedJson \ "type" match {
|
parsedJson \ "type" match {
|
||||||
case JString("message_start") =>
|
case JString("message_start") =>
|
||||||
streamId = (parsedJson \ "message" \ "id").extract[String]
|
streamId = (parsedJson \ "message" \ "id").extract[String]
|
||||||
case JString("content_block_delta") =>
|
case JString("content_block_delta") =>
|
||||||
(parsedJson \ "delta" \ "text") match {
|
(parsedJson \ "delta" \ "text") match {
|
||||||
@@ -102,7 +99,7 @@ class ClaudeServiceImpl(
|
|||||||
completed = false
|
completed = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
case JString("content_block_start") => ()
|
case JString("content_block_start") => ()
|
||||||
case JString("content_block_stop") => ()
|
case JString("content_block_stop") => ()
|
||||||
@@ -116,12 +113,12 @@ class ClaudeServiceImpl(
|
|||||||
completed = true
|
completed = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case JString("error") =>
|
case JString("error") =>
|
||||||
val errorDetails = parsedJson \ "error"
|
val errorDetails = parsedJson \ "error"
|
||||||
val errorType = (errorDetails \ "type").extract[String]
|
val errorType = (errorDetails \ "type").extract[String]
|
||||||
val errorMessage = (errorDetails \ "message").extract[String]
|
val errorMessage = (errorDetails \ "message").extract[String]
|
||||||
println(s"Got an \"($errorType)\" error from Claude: $errorMessage")
|
println(s"Got an \"($errorType)\" error from Claude: $errorMessage")
|
||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,26 +133,24 @@ class ClaudeServiceImpl(
|
|||||||
headers: Map[String, Vector[String]]
|
headers: Map[String, Vector[String]]
|
||||||
): Option[RateLimits] =
|
): Option[RateLimits] =
|
||||||
for {
|
for {
|
||||||
requestLimit <- headers.get("anthropic-ratelimit-requests-limit")
|
requestLimit <- headers.get("anthropic-ratelimit-requests-limit")
|
||||||
tokenLimit <- headers.get("anthropic-ratelimit-tokens-limit")
|
tokenLimit <- headers.get("anthropic-ratelimit-tokens-limit")
|
||||||
requestsRemaining <- headers.get("anthropic-ratelimit-requests-remaining")
|
requestsRemaining <- headers.get("anthropic-ratelimit-requests-remaining")
|
||||||
tokensRemaining <- headers.get("anthropic-ratelimit-tokens-remaining")
|
tokensRemaining <- headers.get("anthropic-ratelimit-tokens-remaining")
|
||||||
requestResetTime <- headers.get("anthropic-ratelimit-requests-reset")
|
requestResetTime <- headers.get("anthropic-ratelimit-requests-reset")
|
||||||
tokenResetTime <- headers.get("anthropic-ratelimit-tokens-reset")
|
tokenResetTime <- headers.get("anthropic-ratelimit-tokens-reset")
|
||||||
} yield {
|
} yield RateLimits(
|
||||||
RateLimits(
|
requestLimit = requestLimit.head.toInt,
|
||||||
requestLimit = requestLimit.head.toInt,
|
tokenLimit = tokenLimit.head.toInt,
|
||||||
tokenLimit = tokenLimit.head.toInt,
|
requestsRemaining = requestsRemaining.head.toInt,
|
||||||
requestsRemaining = requestsRemaining.head.toInt,
|
tokensRemaining = tokensRemaining.head.toInt,
|
||||||
tokensRemaining = tokensRemaining.head.toInt,
|
requestResetTime = ZonedDateTime.parse(
|
||||||
requestResetTime = ZonedDateTime.parse(
|
requestResetTime.head,
|
||||||
requestResetTime.head,
|
rfc3339formatter
|
||||||
rfc3339formatter
|
),
|
||||||
),
|
tokenResetTime = ZonedDateTime.parse(
|
||||||
tokenResetTime = ZonedDateTime.parse(
|
tokenResetTime.head,
|
||||||
tokenResetTime.head,
|
rfc3339formatter
|
||||||
rfc3339formatter
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-23
@@ -1,11 +1,6 @@
|
|||||||
package net.eagle0.common.llm_integration
|
package net.eagle0.common.llm_integration
|
||||||
|
|
||||||
import java.net.http.{
|
import java.net.http.{HttpClient, HttpRequest, HttpResponse, HttpTimeoutException}
|
||||||
HttpClient,
|
|
||||||
HttpRequest,
|
|
||||||
HttpResponse,
|
|
||||||
HttpTimeoutException
|
|
||||||
}
|
|
||||||
import java.net.http.HttpClient.{Redirect, Version}
|
import java.net.http.HttpClient.{Redirect, Version}
|
||||||
import java.net.http.HttpResponse.ResponseInfo
|
import java.net.http.HttpResponse.ResponseInfo
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
@@ -49,10 +44,10 @@ final class ExternalTextGenerationCaller(
|
|||||||
private var inProgressCount = 0
|
private var inProgressCount = 0
|
||||||
|
|
||||||
def maxConcurrentStreams: Int = serviceImpl.maxConcurrentStreams
|
def maxConcurrentStreams: Int = serviceImpl.maxConcurrentStreams
|
||||||
def getInProgressCount: Int = inProgressCount
|
def getInProgressCount: Int = inProgressCount
|
||||||
|
|
||||||
private var currentRateLimits: Option[RateLimits] = None
|
private var currentRateLimits: Option[RateLimits] = None
|
||||||
private var currentRateLimitTime: ZonedDateTime = ZonedDateTime.now()
|
private var currentRateLimitTime: ZonedDateTime = ZonedDateTime.now()
|
||||||
|
|
||||||
def millisUntilReset: Long =
|
def millisUntilReset: Long =
|
||||||
ChronoUnit.MILLIS.between(
|
ChronoUnit.MILLIS.between(
|
||||||
@@ -76,9 +71,8 @@ final class ExternalTextGenerationCaller(
|
|||||||
inputText: String,
|
inputText: String,
|
||||||
partialCompletion: Option[String],
|
partialCompletion: Option[String],
|
||||||
streamingConsumer: Consumer[StreamingTextResults],
|
streamingConsumer: Consumer[StreamingTextResults],
|
||||||
backoffSeconds: Double =
|
backoffSeconds: Double = ExternalTextGenerationCaller.initialBackoffSeconds
|
||||||
ExternalTextGenerationCaller.initialBackoffSeconds
|
): Future[HttpResponse[Unit]] =
|
||||||
): Future[HttpResponse[Unit]] = {
|
|
||||||
streamCompletion(
|
streamCompletion(
|
||||||
inputText = inputText,
|
inputText = inputText,
|
||||||
request = serviceImpl.makeRequest(
|
request = serviceImpl.makeRequest(
|
||||||
@@ -88,7 +82,6 @@ final class ExternalTextGenerationCaller(
|
|||||||
backoffSeconds = backoffSeconds,
|
backoffSeconds = backoffSeconds,
|
||||||
streamingConsumer = streamingConsumer
|
streamingConsumer = streamingConsumer
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
private def streamCompletion(
|
private def streamCompletion(
|
||||||
inputText: String,
|
inputText: String,
|
||||||
@@ -102,20 +95,18 @@ final class ExternalTextGenerationCaller(
|
|||||||
println(s"Trying again after $backoffSeconds seconds...")
|
println(s"Trying again after $backoffSeconds seconds...")
|
||||||
|
|
||||||
val promise = Promise[HttpResponse[Unit]]()
|
val promise = Promise[HttpResponse[Unit]]()
|
||||||
val t = new Timer()
|
val t = new Timer()
|
||||||
t.schedule(
|
t.schedule(
|
||||||
new TimerTask {
|
new TimerTask {
|
||||||
override def run(): Unit = {
|
override def run(): Unit =
|
||||||
promise.completeWith(
|
promise.completeWith(
|
||||||
streamCompletion(
|
streamCompletion(
|
||||||
inputText = inputText,
|
inputText = inputText,
|
||||||
request = request,
|
request = request,
|
||||||
backoffSeconds =
|
backoffSeconds = ExternalTextGenerationCaller.nextBackoff(backoffSeconds),
|
||||||
ExternalTextGenerationCaller.nextBackoff(backoffSeconds),
|
|
||||||
streamingConsumer = streamingConsumer
|
streamingConsumer = streamingConsumer
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
(backoffSeconds * 1000).toLong
|
(backoffSeconds * 1000).toLong
|
||||||
)
|
)
|
||||||
@@ -126,19 +117,19 @@ final class ExternalTextGenerationCaller(
|
|||||||
httpClient
|
httpClient
|
||||||
.sendAsync(
|
.sendAsync(
|
||||||
request,
|
request,
|
||||||
(respInfo: ResponseInfo) => {
|
(respInfo: ResponseInfo) =>
|
||||||
if respInfo.statusCode() == HttpURLConnection.HTTP_OK then
|
if respInfo.statusCode() == HttpURLConnection.HTTP_OK then
|
||||||
new SseSubscriber(serviceImpl.stringConsumer(streamingConsumer))
|
new SseSubscriber(serviceImpl.stringConsumer(streamingConsumer))
|
||||||
else
|
else
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
s"Unexpected response code: ${respInfo.statusCode()}"
|
s"Unexpected response code: ${respInfo.statusCode()}"
|
||||||
)
|
)
|
||||||
}
|
|
||||||
)
|
)
|
||||||
.asScala
|
.asScala
|
||||||
.andThen { case x =>
|
.andThen {
|
||||||
inProgressCount -= 1
|
case x =>
|
||||||
x
|
inProgressCount -= 1
|
||||||
|
x
|
||||||
}
|
}
|
||||||
.transform {
|
.transform {
|
||||||
case Success(httpResponse) =>
|
case Success(httpResponse) =>
|
||||||
@@ -196,7 +187,7 @@ final class ExternalTextGenerationCaller(
|
|||||||
case e: CompletionException =>
|
case e: CompletionException =>
|
||||||
println(s"CompletionException error $e")
|
println(s"CompletionException error $e")
|
||||||
tryAgain()
|
tryAgain()
|
||||||
case e: Throwable =>
|
case e: Throwable =>
|
||||||
println(s"error $e")
|
println(s"error $e")
|
||||||
tryAgain()
|
tryAgain()
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -66,13 +66,11 @@ object ExternalTextGenerationCallerApp {
|
|||||||
|Alakanda's faction controls Atfordia.
|
|Alakanda's faction controls Atfordia.
|
||||||
|Roger the Shrubber's faction controls Chapellia.""".stripMargin
|
|Roger the Shrubber's faction controls Chapellia.""".stripMargin
|
||||||
|
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
val completion = caller.streamCompletion(
|
val completion = caller.streamCompletion(
|
||||||
inputText = inputText,
|
inputText = inputText,
|
||||||
partialCompletion = None,
|
partialCompletion = None,
|
||||||
streamingConsumer = results => {
|
streamingConsumer = results => print(results.value)
|
||||||
print(results.value)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
Await.result(completion, Duration(500, SECONDS))
|
Await.result(completion, Duration(500, SECONDS))
|
||||||
println("")
|
println("")
|
||||||
|
|||||||
+40
-45
@@ -6,10 +6,7 @@ import java.net.http.HttpRequest.BodyPublishers
|
|||||||
import java.time.{Duration, ZonedDateTime}
|
import java.time.{Duration, ZonedDateTime}
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
import net.eagle0.common.llm_integration.OpenAIChatCompletionsServiceImpl.{
|
import net.eagle0.common.llm_integration.OpenAIChatCompletionsServiceImpl.{dictionaryFor, messageVector}
|
||||||
dictionaryFor,
|
|
||||||
messageVector
|
|
||||||
}
|
|
||||||
import org.json4s.{DefaultFormats, JArray, JObject, JString}
|
import org.json4s.{DefaultFormats, JArray, JObject, JString}
|
||||||
import org.json4s.jvalue2extractable
|
import org.json4s.jvalue2extractable
|
||||||
import org.json4s.jvalue2monadic
|
import org.json4s.jvalue2monadic
|
||||||
@@ -18,16 +15,16 @@ import org.json4s.native.{Json, Serialization}
|
|||||||
object OpenAIChatCompletionsServiceImpl {
|
object OpenAIChatCompletionsServiceImpl {
|
||||||
val gpt5: String = "gpt-5"
|
val gpt5: String = "gpt-5"
|
||||||
|
|
||||||
private val apiKey = ApiKeys.openAI
|
private val apiKey = ApiKeys.openAI
|
||||||
private val baseURL = new URL("https://api.openai.com/v1/chat/completions")
|
private val baseURL = new URL("https://api.openai.com/v1/chat/completions")
|
||||||
private val temperature: Double = 1.0
|
private val temperature: Double = 1.0
|
||||||
|
|
||||||
private def dictionaryFor(
|
private def dictionaryFor(
|
||||||
modelName: String
|
modelName: String
|
||||||
): Map[String, Any] = Map(
|
): Map[String, Any] = Map(
|
||||||
"model" -> modelName,
|
"model" -> modelName,
|
||||||
"temperature" -> temperature,
|
"temperature" -> temperature,
|
||||||
"stream" -> true,
|
"stream" -> true,
|
||||||
"reasoning_effort" -> "minimal"
|
"reasoning_effort" -> "minimal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,12 +34,12 @@ object OpenAIChatCompletionsServiceImpl {
|
|||||||
): Vector[Map[String, String]] =
|
): Vector[Map[String, String]] =
|
||||||
Vector(
|
Vector(
|
||||||
Map(
|
Map(
|
||||||
"role" -> "user",
|
"role" -> "user",
|
||||||
"content" -> inputText
|
"content" -> inputText
|
||||||
)
|
)
|
||||||
) ++ partialCompletion.map(pc =>
|
) ++ partialCompletion.map(pc =>
|
||||||
Map(
|
Map(
|
||||||
"role" -> "assistant",
|
"role" -> "assistant",
|
||||||
"content" -> pc
|
"content" -> pc
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -99,18 +96,18 @@ class OpenAIChatCompletionsServiceImpl(
|
|||||||
override def stringConsumer(
|
override def stringConsumer(
|
||||||
streamingConsumer: Consumer[StreamingTextResults]
|
streamingConsumer: Consumer[StreamingTextResults]
|
||||||
): Consumer[String] = (t: String) => {
|
): Consumer[String] = (t: String) => {
|
||||||
val json = new Json(DefaultFormats)
|
val json = new Json(DefaultFormats)
|
||||||
val parsedJson =
|
val parsedJson =
|
||||||
try {
|
try
|
||||||
json.parse(t)
|
json.parse(t)
|
||||||
} catch {
|
catch {
|
||||||
case pe: org.json4s.ParserUtil.ParseException =>
|
case pe: org.json4s.ParserUtil.ParseException =>
|
||||||
println(s"Failed to parse JSON: $t")
|
println(s"Failed to parse JSON: $t")
|
||||||
throw pe
|
throw pe
|
||||||
}
|
}
|
||||||
|
|
||||||
val streamId = (parsedJson \ "id").extract[String]
|
val streamId = (parsedJson \ "id").extract[String]
|
||||||
val choice = (parsedJson \ "choices")
|
val choice = (parsedJson \ "choices")
|
||||||
.asInstanceOf[JArray]
|
.asInstanceOf[JArray]
|
||||||
.arr
|
.arr
|
||||||
.head
|
.head
|
||||||
@@ -126,7 +123,7 @@ class OpenAIChatCompletionsServiceImpl(
|
|||||||
case JString(str) =>
|
case JString(str) =>
|
||||||
println(s"Unexpected finish reason $str")
|
println(s"Unexpected finish reason $str")
|
||||||
true
|
true
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
|
||||||
(choice \ "delta" \ "content") match {
|
(choice \ "delta" \ "content") match {
|
||||||
@@ -138,7 +135,7 @@ class OpenAIChatCompletionsServiceImpl(
|
|||||||
completed = completed
|
completed = completed
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case _ =>
|
case _ =>
|
||||||
if completed then
|
if completed then
|
||||||
streamingConsumer.accept(
|
streamingConsumer.accept(
|
||||||
StreamingTextResults(
|
StreamingTextResults(
|
||||||
@@ -154,32 +151,30 @@ class OpenAIChatCompletionsServiceImpl(
|
|||||||
headers: Map[String, Vector[String]]
|
headers: Map[String, Vector[String]]
|
||||||
): Option[RateLimits] =
|
): Option[RateLimits] =
|
||||||
for {
|
for {
|
||||||
requestLimit <- headers.get("x-ratelimit-limit-requests")
|
requestLimit <- headers.get("x-ratelimit-limit-requests")
|
||||||
tokenLimit <- headers.get("x-ratelimit-limit-tokens")
|
tokenLimit <- headers.get("x-ratelimit-limit-tokens")
|
||||||
requestsRemaining <- headers.get("x-ratelimit-remaining-requests")
|
requestsRemaining <- headers.get("x-ratelimit-remaining-requests")
|
||||||
tokensRemaining <- headers.get("x-ratelimit-remaining-tokens")
|
tokensRemaining <- headers.get("x-ratelimit-remaining-tokens")
|
||||||
requestResetTime <- headers.get("x-ratelimit-reset-requests")
|
requestResetTime <- headers.get("x-ratelimit-reset-requests")
|
||||||
tokenResetTime <- headers.get("x-ratelimit-reset-tokens")
|
tokenResetTime <- headers.get("x-ratelimit-reset-tokens")
|
||||||
} yield {
|
} yield RateLimits(
|
||||||
RateLimits(
|
requestLimit = requestLimit.head.toInt,
|
||||||
requestLimit = requestLimit.head.toInt,
|
tokenLimit = tokenLimit.head.toInt,
|
||||||
tokenLimit = tokenLimit.head.toInt,
|
requestsRemaining = requestsRemaining.head.toInt,
|
||||||
requestsRemaining = requestsRemaining.head.toInt,
|
tokensRemaining = tokensRemaining.head.toInt,
|
||||||
tokensRemaining = tokensRemaining.head.toInt,
|
requestResetTime = ZonedDateTime
|
||||||
requestResetTime = ZonedDateTime
|
.now()
|
||||||
.now()
|
.plus(
|
||||||
.plus(
|
OpenAiDurationParser
|
||||||
OpenAiDurationParser
|
.parseDuration(requestResetTime.head)
|
||||||
.parseDuration(requestResetTime.head)
|
.getOrElse(Duration.ZERO)
|
||||||
.getOrElse(Duration.ZERO)
|
),
|
||||||
),
|
tokenResetTime = ZonedDateTime
|
||||||
tokenResetTime = ZonedDateTime
|
.now()
|
||||||
.now()
|
.plus(
|
||||||
.plus(
|
OpenAiDurationParser
|
||||||
OpenAiDurationParser
|
.parseDuration(tokenResetTime.head)
|
||||||
.parseDuration(tokenResetTime.head)
|
.getOrElse(Duration.ZERO)
|
||||||
.getOrElse(Duration.ZERO)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import java.time.Duration
|
|||||||
|
|
||||||
object OpenAiDurationParser {
|
object OpenAiDurationParser {
|
||||||
private val durationRegex = """(\d+)((ms)|[smhdy])""".r
|
private val durationRegex = """(\d+)((ms)|[smhdy])""".r
|
||||||
private val durationMap = Map(
|
private val durationMap = Map(
|
||||||
"ms" -> Duration.ofMillis(1),
|
"ms" -> Duration.ofMillis(1),
|
||||||
"s" -> Duration.ofSeconds(1),
|
"s" -> Duration.ofSeconds(1),
|
||||||
"m" -> Duration.ofMinutes(1),
|
"m" -> Duration.ofMinutes(1),
|
||||||
"h" -> Duration.ofHours(1),
|
"h" -> Duration.ofHours(1),
|
||||||
"d" -> Duration.ofDays(1),
|
"d" -> Duration.ofDays(1),
|
||||||
"y" -> Duration.ofDays(365)
|
"y" -> Duration.ofDays(365)
|
||||||
)
|
)
|
||||||
|
|
||||||
def parseDuration(duration: String): Option[Duration] =
|
def parseDuration(duration: String): Option[Duration] =
|
||||||
@@ -18,7 +18,7 @@ object OpenAiDurationParser {
|
|||||||
.findAllMatchIn(duration)
|
.findAllMatchIn(duration)
|
||||||
.map { m =>
|
.map { m =>
|
||||||
val amount = m.group(1).toInt
|
val amount = m.group(1).toInt
|
||||||
val unit = m.group(2)
|
val unit = m.group(2)
|
||||||
durationMap(unit).multipliedBy(amount)
|
durationMap(unit).multipliedBy(amount)
|
||||||
}
|
}
|
||||||
.toSeq match {
|
.toSeq match {
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import scala.util.Random
|
|||||||
import net.eagle0.common.{SeededRandom, TsvUtils}
|
import net.eagle0.common.{SeededRandom, TsvUtils}
|
||||||
|
|
||||||
class NameGenerator {
|
class NameGenerator {
|
||||||
private val random = SeededRandom(Random.nextLong())
|
private val random = SeededRandom(Random.nextLong())
|
||||||
private val emptySubstitutions = Map[String, String]()
|
private val emptySubstitutions = Map[String, String]()
|
||||||
private val nameConstructionUrl = new File(
|
private val nameConstructionUrl = new File(
|
||||||
"/opt/nameConstruction.txt"
|
"/opt/nameConstruction.txt"
|
||||||
).toURI.toURL
|
).toURI.toURL
|
||||||
private val namesFileUrl = new File("/opt/names.tsv").toURI.toURL
|
private val namesFileUrl = new File("/opt/names.tsv").toURI.toURL
|
||||||
|
|
||||||
private val namesMap = TsvUtils.loadColumnMaps(namesFileUrl).get
|
private val namesMap = TsvUtils.loadColumnMaps(namesFileUrl).get
|
||||||
private val formatString =
|
private val formatString =
|
||||||
StringConstructionTokenGenerator.formatStringFromUrl(
|
StringConstructionTokenGenerator.formatStringFromUrl(
|
||||||
nameConstructionUrl
|
nameConstructionUrl
|
||||||
@@ -41,17 +41,17 @@ class NameGenerator {
|
|||||||
atSpecializations = Vector("male")
|
atSpecializations = Vector("male")
|
||||||
)
|
)
|
||||||
|
|
||||||
def nextFemaleName(): String =
|
def nextFemaleName(): String =
|
||||||
femaleTok.nextString(random, emptySubstitutions).newValue
|
femaleTok.nextString(random, emptySubstitutions).newValue
|
||||||
def nextMaleName(): String =
|
def nextMaleName(): String =
|
||||||
maleTok.nextString(random, emptySubstitutions).newValue
|
maleTok.nextString(random, emptySubstitutions).newValue
|
||||||
def nextNeutralName(): String =
|
def nextNeutralName(): String =
|
||||||
allTok.nextString(random, emptySubstitutions).newValue
|
allTok.nextString(random, emptySubstitutions).newValue
|
||||||
|
|
||||||
def nextName(): String = {
|
def nextName(): String = {
|
||||||
val intR = random.nextInt(0, 20)
|
val intR = random.nextInt(0, 20)
|
||||||
val newInt = intR.newValue
|
val newInt = intR.newValue
|
||||||
val tok =
|
val tok =
|
||||||
if newInt == 0 then allTok
|
if newInt == 0 then allTok
|
||||||
else if newInt <= 10 then femaleTok
|
else if newInt <= 10 then femaleTok
|
||||||
else maleTok
|
else maleTok
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ class StringConstructionParser(
|
|||||||
import ParseSequence.*
|
import ParseSequence.*
|
||||||
|
|
||||||
private def charString(seq: Seq[CharacterOrToken]): String =
|
private def charString(seq: Seq[CharacterOrToken]): String =
|
||||||
seq.collect { case C(char) =>
|
seq.collect {
|
||||||
char
|
case C(char) =>
|
||||||
|
char
|
||||||
}.mkString
|
}.mkString
|
||||||
|
|
||||||
def parseLiteral(fmt: String): Option[(String, StringConstructionToken)] = {
|
def parseLiteral(fmt: String): Option[(String, StringConstructionToken)] = {
|
||||||
@@ -25,12 +26,11 @@ class StringConstructionParser(
|
|||||||
def parseRemaining(str: String, acc: String): ParseStatus =
|
def parseRemaining(str: String, acc: String): ParseStatus =
|
||||||
str.head match {
|
str.head match {
|
||||||
case '\\' =>
|
case '\\' =>
|
||||||
if str.length < 2 then
|
if str.length < 2 then throw new IllegalArgumentException("Found \\ at end of literal")
|
||||||
throw new IllegalArgumentException("Found \\ at end of literal")
|
|
||||||
else parseRemaining(str.drop(2), acc + str.charAt(1))
|
else parseRemaining(str.drop(2), acc + str.charAt(1))
|
||||||
case '\"' =>
|
case '\"' =>
|
||||||
ParseStatus(str.drop(1), acc)
|
ParseStatus(str.drop(1), acc)
|
||||||
case _ =>
|
case _ =>
|
||||||
parseRemaining(str.drop(1), acc + str.head)
|
parseRemaining(str.drop(1), acc + str.head)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,44 +41,44 @@ class StringConstructionParser(
|
|||||||
|
|
||||||
private def parseOptional(
|
private def parseOptional(
|
||||||
ts: ParseSequence
|
ts: ParseSequence
|
||||||
): Try[(ParseSequence, StringConstructionToken)] = {
|
): Try[(ParseSequence, StringConstructionToken)] =
|
||||||
insideBalanced(ts, open = '{', closed = '}').flatMap {
|
insideBalanced(ts, open = '{', closed = '}').flatMap {
|
||||||
case (remainAfter, inWithOdds) =>
|
case (remainAfter, inWithOdds) =>
|
||||||
val DecimalParseResults(inAfterOdds, odds) = readDecimal(inWithOdds)
|
val DecimalParseResults(inAfterOdds, odds) = readDecimal(inWithOdds)
|
||||||
|
|
||||||
parsePossiblyParsed(inAfterOdds).flatMap { case (rem, parsed) =>
|
parsePossiblyParsed(inAfterOdds).flatMap {
|
||||||
if rem.nonEmpty then
|
case (rem, parsed) =>
|
||||||
Failure(
|
if rem.nonEmpty then
|
||||||
new IllegalArgumentException(
|
Failure(
|
||||||
"Found extra characters inside optional"
|
new IllegalArgumentException(
|
||||||
|
"Found extra characters inside optional"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
else Success((remainAfter, OptionalToken(parsed, odds)))
|
||||||
else Success((remainAfter, OptionalToken(parsed, odds)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private def parseOneof(
|
private def parseOneof(
|
||||||
ts: ParseSequence
|
ts: ParseSequence
|
||||||
): Try[(ParseSequence, StringConstructionToken)] = {
|
): Try[(ParseSequence, StringConstructionToken)] =
|
||||||
insideBalanced(ts, open = '[', closed = ']').map {
|
insideBalanced(ts, open = '[', closed = ']').map {
|
||||||
case (remainAfter, inside) =>
|
case (remainAfter, inside) =>
|
||||||
var entries = Vector.empty[OneofListEntry]
|
var entries = Vector.empty[OneofListEntry]
|
||||||
var pos = inside
|
var pos = inside
|
||||||
while pos.nonEmpty do {
|
while pos.nonEmpty do {
|
||||||
val t = pos.head
|
val t = pos.head
|
||||||
if t == ',' then pos = pos.drop(1)
|
if t == ',' then pos = pos.drop(1)
|
||||||
else {
|
else {
|
||||||
val DecimalParseResults(remIn, weight) = readDecimal(pos)
|
val DecimalParseResults(remIn, weight) = readDecimal(pos)
|
||||||
parsePossiblyParsed(remIn).map { case (rem, parsed) =>
|
parsePossiblyParsed(remIn).map {
|
||||||
entries = entries :+ OneofListEntry(parsed, weight)
|
case (rem, parsed) =>
|
||||||
pos = rem
|
entries = entries :+ OneofListEntry(parsed, weight)
|
||||||
|
pos = rem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(remainAfter, OneofListToken(entries))
|
(remainAfter, OneofListToken(entries))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def parseListSelector(
|
def parseListSelector(
|
||||||
ts: ParseSequence
|
ts: ParseSequence
|
||||||
@@ -112,12 +112,11 @@ class StringConstructionParser(
|
|||||||
|
|
||||||
def parseCapitalized(
|
def parseCapitalized(
|
||||||
ts: ParseSequence
|
ts: ParseSequence
|
||||||
): Try[(ParseSequence, StringConstructionToken)] = {
|
): Try[(ParseSequence, StringConstructionToken)] =
|
||||||
for {
|
for {
|
||||||
(remainAfter, inside) <- insideBalanced(ts, open = '-', closed = '+')
|
(remainAfter, inside) <- insideBalanced(ts, open = '-', closed = '+')
|
||||||
(_, parsed) <- parsePossiblyParsed(inside)
|
(_, parsed) <- parsePossiblyParsed(inside)
|
||||||
} yield (remainAfter, TitleCaseToken(parsed))
|
} yield (remainAfter, TitleCaseToken(parsed))
|
||||||
}
|
|
||||||
|
|
||||||
def parseOrdinal(
|
def parseOrdinal(
|
||||||
ts: ParseSequence
|
ts: ParseSequence
|
||||||
@@ -146,7 +145,7 @@ class StringConstructionParser(
|
|||||||
def go(
|
def go(
|
||||||
tokens: ParseSequence,
|
tokens: ParseSequence,
|
||||||
acc: Seq[StringConstructionToken]
|
acc: Seq[StringConstructionToken]
|
||||||
): Try[(ParseSequence, Seq[StringConstructionToken])] = {
|
): Try[(ParseSequence, Seq[StringConstructionToken])] =
|
||||||
if tokens.isEmpty then Success((Vector.empty, acc))
|
if tokens.isEmpty then Success((Vector.empty, acc))
|
||||||
else {
|
else {
|
||||||
tokens.head match {
|
tokens.head match {
|
||||||
@@ -168,10 +167,10 @@ class StringConstructionParser(
|
|||||||
case T(t) => go(tokens.drop(1), acc :+ t)
|
case T(t) => go(tokens.drop(1), acc :+ t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
go(tokens, Vector.empty).map { case (rem, ts) =>
|
go(tokens, Vector.empty).map {
|
||||||
(rem, if ts.size == 1 then ts.head else SequenceToken(ts))
|
case (rem, ts) =>
|
||||||
|
(rem, if ts.size == 1 then ts.head else SequenceToken(ts))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +179,7 @@ class StringConstructionParser(
|
|||||||
def parseNext(
|
def parseNext(
|
||||||
remainingString: String,
|
remainingString: String,
|
||||||
acc: ParseSequence
|
acc: ParseSequence
|
||||||
): ParseSequence = {
|
): ParseSequence =
|
||||||
if remainingString.isEmpty then acc
|
if remainingString.isEmpty then acc
|
||||||
else
|
else
|
||||||
remainingString.head match {
|
remainingString.head match {
|
||||||
@@ -192,19 +191,18 @@ class StringConstructionParser(
|
|||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
s"Illegal literal in $remainingString"
|
s"Illegal literal in $remainingString"
|
||||||
)
|
)
|
||||||
case _ =>
|
case _ =>
|
||||||
parseNext(
|
parseNext(
|
||||||
remainingString.drop(1),
|
remainingString.drop(1),
|
||||||
acc :+ C(remainingString.head)
|
acc :+ C(remainingString.head)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val tokens = parseNext(formatString, Vector.empty)
|
val tokens = parseNext(formatString, Vector.empty)
|
||||||
parsePossiblyParsed(tokens).flatMap { case (rem, tok) =>
|
parsePossiblyParsed(tokens).flatMap {
|
||||||
if rem.nonEmpty then
|
case (rem, tok) =>
|
||||||
Failure(new IllegalArgumentException("Failed to parse"))
|
if rem.nonEmpty then Failure(new IllegalArgumentException("Failed to parse"))
|
||||||
else Success(tok)
|
else Success(tok)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,8 +211,8 @@ class StringConstructionParser(
|
|||||||
open: Char,
|
open: Char,
|
||||||
closed: Char
|
closed: Char
|
||||||
): Try[(ParseSequence, ParseSequence)] = {
|
): Try[(ParseSequence, ParseSequence)] = {
|
||||||
var pos = ts.drop(1)
|
var pos = ts.drop(1)
|
||||||
var level = 1
|
var level = 1
|
||||||
var inside = Vector.empty[CharacterOrToken]
|
var inside = Vector.empty[CharacterOrToken]
|
||||||
|
|
||||||
while level > 0 && pos.nonEmpty do {
|
while level > 0 && pos.nonEmpty do {
|
||||||
@@ -226,8 +224,7 @@ class StringConstructionParser(
|
|||||||
pos = pos.drop(1)
|
pos = pos.drop(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if level > 0 then
|
if level > 0 then Failure(new IllegalArgumentException("Did not find closing character"))
|
||||||
Failure(new IllegalArgumentException("Did not find closing character"))
|
|
||||||
|
|
||||||
Success((pos, inside))
|
Success((pos, inside))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ object StringConstructionTester {
|
|||||||
"longbowmen"
|
"longbowmen"
|
||||||
)
|
)
|
||||||
types.foreach { typeName =>
|
types.foreach { typeName =>
|
||||||
val adj = s"${typeName}_adj"
|
val adj = s"${typeName}_adj"
|
||||||
val newSubs =
|
val newSubs =
|
||||||
substitutions + ("BATTALION_NAME" -> typeName) + ("BATTALION_ADJECTIVE" -> adj)
|
substitutions + ("BATTALION_NAME" -> typeName) + ("BATTALION_ADJECTIVE" -> adj)
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ object StringConstructionTester {
|
|||||||
val random = new JankyRandom(new Random())
|
val random = new JankyRandom(new Random())
|
||||||
|
|
||||||
print(s"---\n$typeName\n---\n")
|
print(s"---\n$typeName\n---\n")
|
||||||
((1 to 20).toVector).foreach { _ =>
|
(1 to 20).toVector.foreach { _ =>
|
||||||
println(token.nextString(random, Map("PLACE" -> "Pieska")).newValue)
|
println(token.nextString(random, Map("PLACE" -> "Pieska")).newValue)
|
||||||
}
|
}
|
||||||
print("\n\n\n")
|
print("\n\n\n")
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ case class LiteralToken(literal: String) extends StringConstructionToken {
|
|||||||
RandomState(literal, random)
|
RandomState(literal, random)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MissingLiteralKeyException(key: String)
|
class MissingLiteralKeyException(key: String) extends Exception(s"Missing literal key $key") {}
|
||||||
extends Exception(s"Missing literal key $key") {}
|
|
||||||
|
|
||||||
case class SubstitutionToken(key: String) extends StringConstructionToken {
|
case class SubstitutionToken(key: String) extends StringConstructionToken {
|
||||||
override def nextString(
|
override def nextString(
|
||||||
@@ -34,27 +33,25 @@ case class SubstitutionToken(key: String) extends StringConstructionToken {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class SequenceToken(tokens: Seq[StringConstructionToken])
|
case class SequenceToken(tokens: Seq[StringConstructionToken]) extends StringConstructionToken {
|
||||||
extends StringConstructionToken {
|
|
||||||
override def nextString(
|
override def nextString(
|
||||||
random: FunctionalRandom,
|
random: FunctionalRandom,
|
||||||
literalSubstitutions: Map[String, String]
|
literalSubstitutions: Map[String, String]
|
||||||
): RandomState[String] =
|
): RandomState[String] =
|
||||||
tokens.foldLeft(RandomState[String]("", random)) { case (state, tok) =>
|
tokens.foldLeft(RandomState[String]("", random)) {
|
||||||
val nextStr = tok.nextString(state.nextRandom, literalSubstitutions)
|
case (state, tok) =>
|
||||||
RandomState(state.newValue + nextStr.newValue, nextStr.nextRandom)
|
val nextStr = tok.nextString(state.nextRandom, literalSubstitutions)
|
||||||
|
RandomState(state.newValue + nextStr.newValue, nextStr.nextRandom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class OptionalToken(token: StringConstructionToken, odds: Double)
|
case class OptionalToken(token: StringConstructionToken, odds: Double) extends StringConstructionToken {
|
||||||
extends StringConstructionToken {
|
|
||||||
override def nextString(
|
override def nextString(
|
||||||
random: FunctionalRandom,
|
random: FunctionalRandom,
|
||||||
literalSubstitutions: Map[String, String]
|
literalSubstitutions: Map[String, String]
|
||||||
): RandomState[String] = {
|
): RandomState[String] = {
|
||||||
val state = random.nextDouble
|
val state = random.nextDouble
|
||||||
if state.newValue < odds then
|
if state.newValue < odds then token.nextString(state.nextRandom, literalSubstitutions)
|
||||||
token.nextString(state.nextRandom, literalSubstitutions)
|
|
||||||
else RandomState("", state.nextRandom)
|
else RandomState("", state.nextRandom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,33 +64,32 @@ case class OneofListEntry(token: StringConstructionToken, weight: Double) {
|
|||||||
token.nextString(random, literalSubstitutions)
|
token.nextString(random, literalSubstitutions)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class OneofListToken(entries: Seq[OneofListEntry])
|
case class OneofListToken(entries: Seq[OneofListEntry]) extends StringConstructionToken {
|
||||||
extends StringConstructionToken {
|
|
||||||
override def nextString(
|
override def nextString(
|
||||||
random: FunctionalRandom,
|
random: FunctionalRandom,
|
||||||
literalSubstitutions: Map[String, String]
|
literalSubstitutions: Map[String, String]
|
||||||
): RandomState[String] = {
|
): RandomState[String] = {
|
||||||
val totalWeight = entries.map(_.weight).sum
|
val totalWeight = entries.map(_.weight).sum
|
||||||
random.nextDouble.continue { case (double, fr) =>
|
random.nextDouble.continue {
|
||||||
@tailrec
|
case (double, fr) =>
|
||||||
def go(
|
@tailrec
|
||||||
remainingEntries: Seq[OneofListEntry],
|
def go(
|
||||||
remainingD: Double
|
remainingEntries: Seq[OneofListEntry],
|
||||||
): RandomState[String] = remainingEntries match {
|
remainingD: Double
|
||||||
case Nil =>
|
): RandomState[String] = remainingEntries match {
|
||||||
throw new IllegalStateException("Failed to find a valid oneof")
|
case Nil =>
|
||||||
case w +: tail =>
|
throw new IllegalStateException("Failed to find a valid oneof")
|
||||||
if remainingD < w.weight then w.toString(fr, literalSubstitutions)
|
case w +: tail =>
|
||||||
else go(tail, remainingD - w.weight)
|
if remainingD < w.weight then w.toString(fr, literalSubstitutions)
|
||||||
}
|
else go(tail, remainingD - w.weight)
|
||||||
|
}
|
||||||
|
|
||||||
go(entries, double * totalWeight)
|
go(entries, double * totalWeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ListSelectionToken(choices: Seq[String])
|
case class ListSelectionToken(choices: Seq[String]) extends StringConstructionToken {
|
||||||
extends StringConstructionToken {
|
|
||||||
override def nextString(
|
override def nextString(
|
||||||
random: FunctionalRandom,
|
random: FunctionalRandom,
|
||||||
literalSubstitutions: Map[String, String]
|
literalSubstitutions: Map[String, String]
|
||||||
@@ -104,7 +100,7 @@ case class OrdinalSelectionToken(max: Int) extends StringConstructionToken {
|
|||||||
override def nextString(
|
override def nextString(
|
||||||
random: FunctionalRandom,
|
random: FunctionalRandom,
|
||||||
literalSubstitutions: Map[String, String]
|
literalSubstitutions: Map[String, String]
|
||||||
): RandomState[String] = {
|
): RandomState[String] =
|
||||||
random.nextInt(1, max).map { value =>
|
random.nextInt(1, max).map { value =>
|
||||||
if value == 11 then "11th"
|
if value == 11 then "11th"
|
||||||
else {
|
else {
|
||||||
@@ -116,11 +112,9 @@ case class OrdinalSelectionToken(max: Int) extends StringConstructionToken {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class TitleCaseToken(base: StringConstructionToken)
|
case class TitleCaseToken(base: StringConstructionToken) extends StringConstructionToken {
|
||||||
extends StringConstructionToken {
|
|
||||||
val uncapitalizedWords: Set[String] =
|
val uncapitalizedWords: Set[String] =
|
||||||
Set("and", "but", "for", "or", "nor", "the", "a", "an", "to", "as", "of")
|
Set("and", "but", "for", "or", "nor", "the", "a", "an", "to", "as", "of")
|
||||||
|
|
||||||
@@ -128,8 +122,8 @@ case class TitleCaseToken(base: StringConstructionToken)
|
|||||||
words match {
|
words match {
|
||||||
case first +: middle :+ last =>
|
case first +: middle :+ last =>
|
||||||
first.capitalize +: middle :+ last.capitalize
|
first.capitalize +: middle :+ last.capitalize
|
||||||
case Vector(only: String) => Vector(only.capitalize)
|
case Vector(only: String) => Vector(only.capitalize)
|
||||||
case Vector() => Vector()
|
case Vector() => Vector()
|
||||||
}
|
}
|
||||||
|
|
||||||
private def byWordCapitalized(string: String): Vector[String] =
|
private def byWordCapitalized(string: String): Vector[String] =
|
||||||
@@ -141,9 +135,8 @@ case class TitleCaseToken(base: StringConstructionToken)
|
|||||||
override def nextString(
|
override def nextString(
|
||||||
random: FunctionalRandom,
|
random: FunctionalRandom,
|
||||||
literalSubstitutions: Map[String, String]
|
literalSubstitutions: Map[String, String]
|
||||||
): RandomState[String] = {
|
): RandomState[String] =
|
||||||
base.nextString(random, literalSubstitutions).map { cap =>
|
base.nextString(random, literalSubstitutions).map { cap =>
|
||||||
firstAndLastCapitalized(byWordCapitalized(cap)).mkString(" ")
|
firstAndLastCapitalized(byWordCapitalized(cap)).mkString(" ")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-11
@@ -23,8 +23,8 @@ object StringConstructionTokenGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val (stringsMap, unfilteredStringsMap) = {
|
val (stringsMap, unfilteredStringsMap) = {
|
||||||
val allSpecializations = namesMap
|
val allSpecializations = namesMap.map {
|
||||||
.map { case (key, words) =>
|
case (key, words) =>
|
||||||
key.split("@", 2) match {
|
key.split("@", 2) match {
|
||||||
case Array(realKey) => (realKey, "", words)
|
case Array(realKey) => (realKey, "", words)
|
||||||
case Array(realKey, spec) => (realKey, spec, words)
|
case Array(realKey, spec) => (realKey, spec, words)
|
||||||
@@ -33,17 +33,14 @@ object StringConstructionTokenGenerator {
|
|||||||
"Too many @s in column name"
|
"Too many @s in column name"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}.toVector.groupBy(_._1)
|
||||||
.toVector
|
|
||||||
.groupBy { _._1 }
|
|
||||||
|
|
||||||
val stringsMap = allSpecializations
|
val stringsMap = allSpecializations.map {
|
||||||
.map { case (realWord, specializations) =>
|
case (realWord, specializations) =>
|
||||||
val includedSpecializations = specializations.filter(spec =>
|
val includedSpecializations =
|
||||||
spec._2.isEmpty || atSpecializations.contains(spec._2)
|
specializations.filter(spec => spec._2.isEmpty || atSpecializations.contains(spec._2))
|
||||||
)
|
|
||||||
(realWord, includedSpecializations.flatMap(_._3))
|
(realWord, includedSpecializations.flatMap(_._3))
|
||||||
}
|
}
|
||||||
|
|
||||||
val unfilteredStringsMap = allSpecializations.map {
|
val unfilteredStringsMap = allSpecializations.map {
|
||||||
case (realWord, specializations) =>
|
case (realWord, specializations) =>
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import scala.annotation.tailrec
|
|||||||
// Functions for parsing events from an SSE stream. Uses spec as defined in
|
// Functions for parsing events from an SSE stream. Uses spec as defined in
|
||||||
// https://html.spec.whatwg.org/multipage/server-sent-events.html
|
// https://html.spec.whatwg.org/multipage/server-sent-events.html
|
||||||
object SseEventReader {
|
object SseEventReader {
|
||||||
private val DataKey = "data"
|
private val DataKey = "data"
|
||||||
private val EventKey = "event"
|
private val EventKey = "event"
|
||||||
private val IdKey = "id"
|
private val IdKey = "id"
|
||||||
private val RetryKey = "retry"
|
private val RetryKey = "retry"
|
||||||
private val DefaultEventValue = "message"
|
private val DefaultEventValue = "message"
|
||||||
|
|
||||||
sealed trait ReadResult
|
sealed trait ReadResult
|
||||||
@@ -36,8 +36,7 @@ object SseEventReader {
|
|||||||
else
|
else
|
||||||
readLines(
|
readLines(
|
||||||
IncompleteEvent(
|
IncompleteEvent(
|
||||||
eventType =
|
eventType = carryoverEvent.map(_.eventType).getOrElse(DefaultEventValue),
|
||||||
carryoverEvent.map(_.eventType).getOrElse(DefaultEventValue),
|
|
||||||
metadata = carryoverEvent.map(_.metadata).getOrElse(Map.empty),
|
metadata = carryoverEvent.map(_.metadata).getOrElse(Map.empty),
|
||||||
data = carryoverEvent.map(_.data).getOrElse(""),
|
data = carryoverEvent.map(_.data).getOrElse(""),
|
||||||
carryover = carryoverEvent.map(_.carryover).getOrElse("") + text
|
carryover = carryoverEvent.map(_.carryover).getOrElse("") + text
|
||||||
@@ -53,14 +52,14 @@ object SseEventReader {
|
|||||||
if value.startsWith(" ") then value.drop(1) else value
|
if value.startsWith(" ") then value.drop(1) else value
|
||||||
|
|
||||||
key match {
|
key match {
|
||||||
case DataKey =>
|
case DataKey =>
|
||||||
IncompleteEvent(
|
IncompleteEvent(
|
||||||
eventType = acc.eventType,
|
eventType = acc.eventType,
|
||||||
metadata = acc.metadata,
|
metadata = acc.metadata,
|
||||||
data = acc.data + trimmedSpaceValue,
|
data = acc.data + trimmedSpaceValue,
|
||||||
carryover = acc.carryover
|
carryover = acc.carryover
|
||||||
)
|
)
|
||||||
case EventKey =>
|
case EventKey =>
|
||||||
IncompleteEvent(
|
IncompleteEvent(
|
||||||
eventType = trimmedSpaceValue,
|
eventType = trimmedSpaceValue,
|
||||||
metadata = acc.metadata,
|
metadata = acc.metadata,
|
||||||
@@ -74,23 +73,23 @@ object SseEventReader {
|
|||||||
data = acc.data,
|
data = acc.data,
|
||||||
carryover = acc.carryover
|
carryover = acc.carryover
|
||||||
)
|
)
|
||||||
case _ =>
|
case _ =>
|
||||||
// The spec actually says to ignore fields except if the key is "event", "data", "id", or "retry"
|
// The spec actually says to ignore fields except if the key is "event", "data", "id", or "retry"
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@tailrec
|
@tailrec
|
||||||
private def readLines(acc: IncompleteEvent): ReadResult = {
|
private def readLines(acc: IncompleteEvent): ReadResult =
|
||||||
acc.carryover.split("\n", 2) match {
|
acc.carryover.split("\n", 2) match {
|
||||||
case Array("", moreText) => // blank line indicates end of event
|
case Array("", moreText) => // blank line indicates end of event
|
||||||
CompleteEvent(
|
CompleteEvent(
|
||||||
eventType = acc.eventType,
|
eventType = acc.eventType,
|
||||||
metadata = acc.metadata,
|
metadata = acc.metadata,
|
||||||
data = acc.data,
|
data = acc.data,
|
||||||
remainingText = moreText
|
remainingText = moreText
|
||||||
)
|
)
|
||||||
case Array(singleLine) => // did not end in a newline
|
case Array(singleLine) => // did not end in a newline
|
||||||
IncompleteEvent(
|
IncompleteEvent(
|
||||||
eventType = acc.eventType,
|
eventType = acc.eventType,
|
||||||
metadata = acc.metadata,
|
metadata = acc.metadata,
|
||||||
@@ -101,7 +100,7 @@ object SseEventReader {
|
|||||||
case Array(kv, newCarryover) =>
|
case Array(kv, newCarryover) =>
|
||||||
val accWithNewCarryover = acc.copy(carryover = newCarryover)
|
val accWithNewCarryover = acc.copy(carryover = newCarryover)
|
||||||
kv.split(":", 2) match {
|
kv.split(":", 2) match {
|
||||||
case Array("", _) =>
|
case Array("", _) =>
|
||||||
// Line started with a colon; ignore line
|
// Line started with a colon; ignore line
|
||||||
readLines(accWithNewCarryover)
|
readLines(accWithNewCarryover)
|
||||||
case Array(key, value) =>
|
case Array(key, value) =>
|
||||||
@@ -109,14 +108,13 @@ object SseEventReader {
|
|||||||
readLines(
|
readLines(
|
||||||
withSingleKeyValuePair(accWithNewCarryover, key, value)
|
withSingleKeyValuePair(accWithNewCarryover, key, value)
|
||||||
)
|
)
|
||||||
case Array(key) =>
|
case Array(key) =>
|
||||||
// Line contained a key with no value, use empty string
|
// Line contained a key with no value, use empty string
|
||||||
readLines(
|
readLines(
|
||||||
withSingleKeyValuePair(accWithNewCarryover, key, "")
|
withSingleKeyValuePair(accWithNewCarryover, key, "")
|
||||||
)
|
)
|
||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,13 @@ import scala.jdk.CollectionConverters.CollectionHasAsScala
|
|||||||
|
|
||||||
import org.json4s.ParserUtil
|
import org.json4s.ParserUtil
|
||||||
|
|
||||||
class SseException(message: String, cause: Throwable = null)
|
class SseException(message: String, cause: Throwable = null) extends Exception(message, cause)
|
||||||
extends Exception(message, cause)
|
|
||||||
|
|
||||||
class SseSubscriber(messageDataConsumer: Consumer[String])
|
class SseSubscriber(messageDataConsumer: Consumer[String]) extends BodySubscriber[Unit] {
|
||||||
extends BodySubscriber[Unit] {
|
|
||||||
@volatile private var subscription: Flow.Subscription = _
|
@volatile private var subscription: Flow.Subscription = _
|
||||||
private val future: CompletableFuture[Unit] = new CompletableFuture[Unit]
|
private val future: CompletableFuture[Unit] = new CompletableFuture[Unit]
|
||||||
|
|
||||||
private val DoneToken = "[DONE]"
|
private val DoneToken = "[DONE]"
|
||||||
private val MessageType = "message"
|
private val MessageType = "message"
|
||||||
|
|
||||||
override def getBody: CompletionStage[Unit] = future
|
override def getBody: CompletionStage[Unit] = future
|
||||||
@@ -42,7 +40,7 @@ class SseSubscriber(messageDataConsumer: Consumer[String])
|
|||||||
|
|
||||||
@volatile private var incompleteText: Option[SseEventReader.IncompleteEvent] =
|
@volatile private var incompleteText: Option[SseEventReader.IncompleteEvent] =
|
||||||
None
|
None
|
||||||
override def onNext(buffers: util.List[ByteBuffer]): Unit = try {
|
override def onNext(buffers: util.List[ByteBuffer]): Unit = try {
|
||||||
val newText = buffers.asScala
|
val newText = buffers.asScala
|
||||||
.map(StandardCharsets.UTF_8.decode)
|
.map(StandardCharsets.UTF_8.decode)
|
||||||
.mkString
|
.mkString
|
||||||
@@ -51,7 +49,7 @@ class SseSubscriber(messageDataConsumer: Consumer[String])
|
|||||||
def consumeEvents(
|
def consumeEvents(
|
||||||
incompleteEvent: Option[SseEventReader.IncompleteEvent],
|
incompleteEvent: Option[SseEventReader.IncompleteEvent],
|
||||||
remainingText: String
|
remainingText: String
|
||||||
): Option[SseEventReader.IncompleteEvent] = {
|
): Option[SseEventReader.IncompleteEvent] =
|
||||||
SseEventReader.readOneEvent(incompleteEvent, remainingText) match {
|
SseEventReader.readOneEvent(incompleteEvent, remainingText) match {
|
||||||
case SseEventReader.EndOfStream =>
|
case SseEventReader.EndOfStream =>
|
||||||
None
|
None
|
||||||
@@ -92,7 +90,6 @@ class SseSubscriber(messageDataConsumer: Consumer[String])
|
|||||||
case incomplete: SseEventReader.IncompleteEvent =>
|
case incomplete: SseEventReader.IncompleteEvent =>
|
||||||
Some(incomplete)
|
Some(incomplete)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.incompleteText = consumeEvents(this.incompleteText, newText)
|
this.incompleteText = consumeEvents(this.incompleteText, newText)
|
||||||
|
|
||||||
@@ -107,7 +104,7 @@ class SseSubscriber(messageDataConsumer: Consumer[String])
|
|||||||
println(fullBuffer)
|
println(fullBuffer)
|
||||||
future.completeExceptionally(pe)
|
future.completeExceptionally(pe)
|
||||||
subscription.cancel()
|
subscription.cancel()
|
||||||
case e: Exception =>
|
case e: Exception =>
|
||||||
future.completeExceptionally(e)
|
future.completeExceptionally(e)
|
||||||
subscription.cancel()
|
subscription.cancel()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import net.eagle0.eagle.service.*
|
|||||||
object Main {
|
object Main {
|
||||||
import ServerSetupHelpers.*
|
import ServerSetupHelpers.*
|
||||||
|
|
||||||
private val eagleGrpcPortKey = Symbol("eagleGrpcPort")
|
private val eagleGrpcPortKey = Symbol("eagleGrpcPort")
|
||||||
private val defaultEagleGrpcPort = "40032"
|
private val defaultEagleGrpcPort = "40032"
|
||||||
|
|
||||||
private val shardokInterfaceRemoteAddressKey = Symbol(
|
private val shardokInterfaceRemoteAddressKey = Symbol(
|
||||||
"shardokInterfaceRemoteAddress"
|
"shardokInterfaceRemoteAddress"
|
||||||
)
|
)
|
||||||
private val defaultShardokInterfaceRemoteAddress = "eagle0.net:443"
|
private val defaultShardokInterfaceRemoteAddress = "eagle0.net:443"
|
||||||
@@ -26,26 +26,25 @@ object Main {
|
|||||||
def nextOption(
|
def nextOption(
|
||||||
map: Map[Symbol, String],
|
map: Map[Symbol, String],
|
||||||
list: Vector[String]
|
list: Vector[String]
|
||||||
): Map[Symbol, String] = {
|
): Map[Symbol, String] =
|
||||||
list match {
|
list match {
|
||||||
case "--eagle-grpc-port" +: value +: tail =>
|
case "--eagle-grpc-port" +: value +: tail =>
|
||||||
nextOption(map ++ Map(eagleGrpcPortKey -> value), tail)
|
nextOption(map ++ Map(eagleGrpcPortKey -> value), tail)
|
||||||
case "--shardok-interface-remote-address" +: value +: tail =>
|
case "--shardok-interface-remote-address" +: value +: tail =>
|
||||||
nextOption(
|
nextOption(
|
||||||
map ++ Map(shardokInterfaceRemoteAddressKey -> value),
|
map ++ Map(shardokInterfaceRemoteAddressKey -> value),
|
||||||
tail
|
tail
|
||||||
)
|
)
|
||||||
case "--gpt-model-name" +: value +: tail =>
|
case "--gpt-model-name" +: value +: tail =>
|
||||||
nextOption(
|
nextOption(
|
||||||
map ++ Map(gptModelNameKey -> value),
|
map ++ Map(gptModelNameKey -> value),
|
||||||
tail
|
tail
|
||||||
)
|
)
|
||||||
case option +: _ =>
|
case option +: _ =>
|
||||||
throw new IllegalArgumentException("Unknown option " + option)
|
throw new IllegalArgumentException("Unknown option " + option)
|
||||||
|
|
||||||
case _ => map
|
case _ => map
|
||||||
}
|
}
|
||||||
}
|
|
||||||
nextOption(Map(), args.toVector)
|
nextOption(Map(), args.toVector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ object RemoveActions {
|
|||||||
val persister = new LocalFilePersister(
|
val persister = new LocalFilePersister(
|
||||||
path + gameId.toHexString
|
path + gameId.toHexString
|
||||||
)
|
)
|
||||||
val history =
|
val history =
|
||||||
PersistedHistory.gameHistoryFromPersister(gameId).get
|
PersistedHistory.gameHistoryFromPersister(gameId).get
|
||||||
|
|
||||||
val found = history.recentHistory.indexWhere(
|
val found = history.recentHistory.indexWhere(
|
||||||
@@ -53,8 +53,8 @@ object RemoveActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def main(args: Array[String]): Unit = {
|
def main(args: Array[String]): Unit = {
|
||||||
val path = args(0)
|
val path = args(0)
|
||||||
val gameId = BigInt(args(1), 16).toLong
|
val gameId = BigInt(args(1), 16).toLong
|
||||||
val countToTruncate = args(2).toInt
|
val countToTruncate = args(2).toInt
|
||||||
|
|
||||||
truncate(path = path, gameId = gameId, countToTruncate = countToTruncate)
|
truncate(path = path, gameId = gameId, countToTruncate = countToTruncate)
|
||||||
|
|||||||
@@ -18,14 +18,13 @@ object MessageComparators {
|
|||||||
acc: Vector[FieldDescriptor] = Vector()
|
acc: Vector[FieldDescriptor] = Vector()
|
||||||
): Vector[FieldDescriptor] = fieldNames match {
|
): Vector[FieldDescriptor] = fieldNames match {
|
||||||
case name +: tail =>
|
case name +: tail =>
|
||||||
val field = {
|
val field =
|
||||||
comp.scalaDescriptor.fields
|
comp.scalaDescriptor.fields
|
||||||
.find(_.name == name)
|
.find(_.name == name)
|
||||||
.getOrElse {
|
.getOrElse {
|
||||||
println(s"No field $name in ${comp.scalaDescriptor.name}")
|
println(s"No field $name in ${comp.scalaDescriptor.name}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if tail.isEmpty then acc :+ field
|
if tail.isEmpty then acc :+ field
|
||||||
else
|
else
|
||||||
@@ -34,7 +33,7 @@ object MessageComparators {
|
|||||||
tail,
|
tail,
|
||||||
acc :+ field
|
acc :+ field
|
||||||
)
|
)
|
||||||
case _ => acc
|
case _ => acc
|
||||||
}
|
}
|
||||||
|
|
||||||
def nestedPValue(
|
def nestedPValue(
|
||||||
@@ -50,7 +49,7 @@ object MessageComparators {
|
|||||||
gm.getFieldByNumber(fd.number).asInstanceOf[GeneratedMessage],
|
gm.getFieldByNumber(fd.number).asInstanceOf[GeneratedMessage],
|
||||||
tail
|
tail
|
||||||
)
|
)
|
||||||
case _ => gm.toPMessage
|
case _ => gm.toPMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
def typedComparison[T](
|
def typedComparison[T](
|
||||||
@@ -59,19 +58,17 @@ object MessageComparators {
|
|||||||
value: String
|
value: String
|
||||||
): (GeneratedMessage => Boolean) = comparator match {
|
): (GeneratedMessage => Boolean) = comparator match {
|
||||||
case "=" =>
|
case "=" =>
|
||||||
(
|
gm =>
|
||||||
gm =>
|
nestedPValue(gm, fieldDescriptors) match {
|
||||||
nestedPValue(gm, fieldDescriptors) match {
|
case PInt(i) => i == value.toInt
|
||||||
case PInt(i) => i == value.toInt
|
case PDouble(d) => d == value.toDouble
|
||||||
case PDouble(d) => d == value.toDouble
|
case PString(s) => s == value
|
||||||
case PString(s) => s == value
|
case PBoolean(b) => b == value.toBoolean
|
||||||
case PBoolean(b) => b == value.toBoolean
|
case PEmpty => value.isEmpty
|
||||||
case PEmpty => value.isEmpty
|
case x =>
|
||||||
case x =>
|
throw new NotImplementedError(s"$x pvalue not implemented")
|
||||||
throw new NotImplementedError(s"$x pvalue not implemented")
|
}
|
||||||
}
|
case x => throw new NotImplementedError(s"$x comparator not implemented")
|
||||||
)
|
|
||||||
case x => throw new NotImplementedError(s"$x comparator not implemented")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,9 +77,9 @@ object GameStateFilters {
|
|||||||
|
|
||||||
def filter(filter: String): ActionWithResultingState => Boolean = {
|
def filter(filter: String): ActionWithResultingState => Boolean = {
|
||||||
val (left, rightWithComparator) = filter.span(!comparisonChars.contains(_))
|
val (left, rightWithComparator) = filter.span(!comparisonChars.contains(_))
|
||||||
val (comparator, right) = rightWithComparator.splitAt(1)
|
val (comparator, right) = rightWithComparator.splitAt(1)
|
||||||
|
|
||||||
val fieldNames = left.split('.').toVector
|
val fieldNames = left.split('.').toVector
|
||||||
val (prefix, remaining) = fieldNames.splitAt(1)
|
val (prefix, remaining) = fieldNames.splitAt(1)
|
||||||
prefix.head match {
|
prefix.head match {
|
||||||
case "gs" =>
|
case "gs" =>
|
||||||
@@ -121,11 +118,11 @@ object SavedGameUtils {
|
|||||||
def filterOne(
|
def filterOne(
|
||||||
results: Vector[ActionWithResultingState],
|
results: Vector[ActionWithResultingState],
|
||||||
filter: String
|
filter: String
|
||||||
): Vector[ActionWithResultingState] = {
|
): Vector[ActionWithResultingState] =
|
||||||
results.filter { case awrs =>
|
results.filter {
|
||||||
GameStateFilters.filter(filter)(awrs)
|
case awrs =>
|
||||||
|
GameStateFilters.filter(filter)(awrs)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def filteredResults(
|
def filteredResults(
|
||||||
results: Vector[ActionWithResultingState],
|
results: Vector[ActionWithResultingState],
|
||||||
@@ -141,9 +138,9 @@ object SavedGameUtils {
|
|||||||
def fieldValues(
|
def fieldValues(
|
||||||
results: Vector[ActionWithResultingState],
|
results: Vector[ActionWithResultingState],
|
||||||
fields: Vector[String]
|
fields: Vector[String]
|
||||||
): Vector[FieldWithValue] = {
|
): Vector[FieldWithValue] =
|
||||||
for {
|
for {
|
||||||
result <- results
|
result <- results
|
||||||
qualifiedFieldName <- fields
|
qualifiedFieldName <- fields
|
||||||
} yield {
|
} yield {
|
||||||
val (ident, subfieldNames) = qualifiedFieldName.split('.').splitAt(1)
|
val (ident, subfieldNames) = qualifiedFieldName.split('.').splitAt(1)
|
||||||
@@ -172,7 +169,6 @@ object SavedGameUtils {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def main(args: Array[String]): Unit = {
|
def main(args: Array[String]): Unit = {
|
||||||
val argList = args.toList
|
val argList = args.toList
|
||||||
@@ -207,8 +203,7 @@ object SavedGameUtils {
|
|||||||
)
|
)
|
||||||
|
|
||||||
def withoutFilterIndices(indices: Vector[Int]): FilteredResultsState = {
|
def withoutFilterIndices(indices: Vector[Int]): FilteredResultsState = {
|
||||||
val newFilters = filters.zipWithIndex
|
val newFilters = filters.zipWithIndex.filterNot { case (_, idx) => indices.contains(idx) }
|
||||||
.filterNot { case (_, idx) => indices.contains(idx) }
|
|
||||||
.map(_._1)
|
.map(_._1)
|
||||||
this.copy(
|
this.copy(
|
||||||
filters = newFilters,
|
filters = newFilters,
|
||||||
@@ -218,8 +213,7 @@ object SavedGameUtils {
|
|||||||
|
|
||||||
def withoutPrintIndices(indices: Vector[Int]): FilteredResultsState =
|
def withoutPrintIndices(indices: Vector[Int]): FilteredResultsState =
|
||||||
this.copy(
|
this.copy(
|
||||||
prints = prints.zipWithIndex
|
prints = prints.zipWithIndex.filterNot { case (_, idx) => indices.contains(idx) }
|
||||||
.filterNot { case (_, idx) => indices.contains(idx) }
|
|
||||||
.map(_._1)
|
.map(_._1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -228,12 +222,14 @@ object SavedGameUtils {
|
|||||||
state: FilteredResultsState
|
state: FilteredResultsState
|
||||||
): FilteredResultsState = {
|
): FilteredResultsState = {
|
||||||
println("Your filters:")
|
println("Your filters:")
|
||||||
state.filters.zipWithIndex.foreach { case (filt, idx) =>
|
state.filters.zipWithIndex.foreach {
|
||||||
println(s" ($idx) $filt")
|
case (filt, idx) =>
|
||||||
|
println(s" ($idx) $filt")
|
||||||
}
|
}
|
||||||
println("Your prints:")
|
println("Your prints:")
|
||||||
state.prints.zipWithIndex.foreach { case (pf, idx) =>
|
state.prints.zipWithIndex.foreach {
|
||||||
println(s" ($idx) $pf")
|
case (pf, idx) =>
|
||||||
|
println(s" ($idx) $pf")
|
||||||
}
|
}
|
||||||
println(s"${state.partialResults.size} filtered results")
|
println(s"${state.partialResults.size} filtered results")
|
||||||
|
|
||||||
@@ -248,8 +244,8 @@ object SavedGameUtils {
|
|||||||
state.withoutFilterIndices(idxStrings.map(_.toInt).toVector)
|
state.withoutFilterIndices(idxStrings.map(_.toInt).toVector)
|
||||||
case "dp" :: idxStrings =>
|
case "dp" :: idxStrings =>
|
||||||
state.withoutPrintIndices(idxStrings.map(_.toInt).toVector)
|
state.withoutPrintIndices(idxStrings.map(_.toInt).toVector)
|
||||||
case "q" :: _ => sys.exit(0)
|
case "q" :: _ => sys.exit(0)
|
||||||
case x :: _ =>
|
case x :: _ =>
|
||||||
println(s"Unknown command $x, stopping")
|
println(s"Unknown command $x, stopping")
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ object TestAIGame {
|
|||||||
|
|
||||||
(1 to gameCount).foreach { _ =>
|
(1 to gameCount).foreach { _ =>
|
||||||
gamesManager.synchronizedCreateGame(
|
gamesManager.synchronizedCreateGame(
|
||||||
expandedGameParameters =
|
expandedGameParameters = GameParametersUtils.defaultExpandedGameParameters,
|
||||||
GameParametersUtils.defaultExpandedGameParameters,
|
|
||||||
gameId = Random.nextLong(),
|
gameId = Random.nextLong(),
|
||||||
humanPlayers = Vector.empty,
|
humanPlayers = Vector.empty,
|
||||||
aiPlayerCount = 5
|
aiPlayerCount = 5
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ case class AIClient(
|
|||||||
gs: GameState,
|
gs: GameState,
|
||||||
functionalRandom: FunctionalRandom
|
functionalRandom: FunctionalRandom
|
||||||
): RandomState[AIClientWithSelectedCommand] = {
|
): RandomState[AIClientWithSelectedCommand] = {
|
||||||
val opac = acs.head._2
|
val opac = acs.head._2
|
||||||
val oneProvinceAcs = acs.head._2.commands
|
val oneProvinceAcs = acs.head._2.commands
|
||||||
|
|
||||||
CommandChooser.choose(
|
CommandChooser.choose(
|
||||||
@@ -215,7 +215,7 @@ case class AIClient(
|
|||||||
) match {
|
) match {
|
||||||
case RandomState(Some(cs), fr) =>
|
case RandomState(Some(cs), fr) =>
|
||||||
RandomState(AIClientWithSelectedCommand(this, Some(cs)), fr)
|
RandomState(AIClientWithSelectedCommand(this, Some(cs)), fr)
|
||||||
case RandomState(None, fr) =>
|
case RandomState(None, fr) =>
|
||||||
if EarlyGameAIClient.isEarlyGame(gs, factionId) then
|
if EarlyGameAIClient.isEarlyGame(gs, factionId) then
|
||||||
EarlyGameAIClient
|
EarlyGameAIClient
|
||||||
.chooseEarlyGameCommand(
|
.chooseEarlyGameCommand(
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ object AIClientUtils {
|
|||||||
gs
|
gs
|
||||||
)).max(0)
|
)).max(0)
|
||||||
|
|
||||||
def desiredHeroCount(pid: ProvinceId, fid: FactionId, gs: GameState): Int = {
|
def desiredHeroCount(pid: ProvinceId, fid: FactionId, gs: GameState): Int =
|
||||||
if LegacyFactionUtils.hostileNeighbors(pid, fid, gs).nonEmpty then 3
|
if LegacyFactionUtils.hostileNeighbors(pid, fid, gs).nonEmpty then 3
|
||||||
else if gs.provinces(pid).support < 65 then 2
|
else if gs.provinces(pid).support < 65 then 2
|
||||||
else 1
|
else 1
|
||||||
}
|
|
||||||
|
|
||||||
def desiredCountForMarchTowardFocus(
|
def desiredCountForMarchTowardFocus(
|
||||||
originProvince: Province,
|
originProvince: Province,
|
||||||
@@ -55,8 +54,7 @@ object AIClientUtils {
|
|||||||
originProvince = originProvince,
|
originProvince = originProvince,
|
||||||
destinationProvince = destinationProvince,
|
destinationProvince = destinationProvince,
|
||||||
availableHeroIds = availableHeroIds,
|
availableHeroIds = availableHeroIds,
|
||||||
factionLeaders =
|
factionLeaders = gs.factions(originProvince.rulingFactionId.get).leaders.toVector,
|
||||||
gs.factions(originProvince.rulingFactionId.get).leaders.toVector,
|
|
||||||
favorLeaders = favorLeaders
|
favorLeaders = favorLeaders
|
||||||
),
|
),
|
||||||
gs,
|
gs,
|
||||||
@@ -78,8 +76,7 @@ object AIClientUtils {
|
|||||||
availableHeroes
|
availableHeroes
|
||||||
.sortBy(hero =>
|
.sortBy(hero =>
|
||||||
(
|
(
|
||||||
if favorLeaders then
|
if favorLeaders then !LegacyFactionUtils.isFactionLeader(hero.id, gs)
|
||||||
!LegacyFactionUtils.isFactionLeader(hero.id, gs)
|
|
||||||
else LegacyFactionUtils.isFactionLeader(hero.id, gs),
|
else LegacyFactionUtils.isFactionLeader(hero.id, gs),
|
||||||
-LegacyHeroUtils.power(hero)
|
-LegacyHeroUtils.power(hero)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ object EarlyGameAIClient {
|
|||||||
gs: GameState,
|
gs: GameState,
|
||||||
acs: Vector[AvailableCommand],
|
acs: Vector[AvailableCommand],
|
||||||
functionalRandom: FunctionalRandom
|
functionalRandom: FunctionalRandom
|
||||||
): RandomState[CommandSelection] = {
|
): RandomState[CommandSelection] =
|
||||||
CommandChooser
|
CommandChooser
|
||||||
.choose(
|
.choose(
|
||||||
actingFactionId = actingFactionId,
|
actingFactionId = actingFactionId,
|
||||||
@@ -40,5 +40,4 @@ object EarlyGameAIClient {
|
|||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.map(_.get)
|
.map(_.get)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,7 @@ object FactionLeaderProvinceRanker {
|
|||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
gameState: GameState
|
gameState: GameState
|
||||||
): Ordering[Province] = Ordering
|
): Ordering[Province] = Ordering
|
||||||
.by((p: Province) =>
|
.by((p: Province) => LegacyFactionUtils.hostileNeighbors(p, factionId, gameState).size)
|
||||||
LegacyFactionUtils.hostileNeighbors(p, factionId, gameState).size
|
|
||||||
)
|
|
||||||
.reverse
|
.reverse
|
||||||
|
|
||||||
// Orders by most-neutral-neighbors-first
|
// Orders by most-neutral-neighbors-first
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package net.eagle0.eagle.ai
|
package net.eagle0.eagle.ai
|
||||||
|
|
||||||
import net.eagle0.common.{FunctionalRandom, RandomState}
|
import net.eagle0.common.{FunctionalRandom, RandomState}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand}
|
||||||
AvailableCommand,
|
|
||||||
MarchAvailableCommand
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
||||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
@@ -23,53 +20,53 @@ object FixLeaderAloneCommandSelector {
|
|||||||
randomSelectionForType[MarchAvailableCommand](
|
randomSelectionForType[MarchAvailableCommand](
|
||||||
availableCommands,
|
availableCommands,
|
||||||
functionalRandom
|
functionalRandom
|
||||||
) { case (ac, frOuter) =>
|
) {
|
||||||
frOuter.nextFlatCollectFirst(
|
case (ac, frOuter) =>
|
||||||
ac.oneProvinceCommands
|
frOuter.nextFlatCollectFirst(
|
||||||
.filter(opmc =>
|
ac.oneProvinceCommands
|
||||||
gameState
|
.filter(opmc =>
|
||||||
.provinces(opmc.originProvinceId)
|
gameState
|
||||||
.rulingFactionHeroIds
|
.provinces(opmc.originProvinceId)
|
||||||
.filterNot(LegacyFactionUtils.isFactionLeader(_, gameState))
|
.rulingFactionHeroIds
|
||||||
.size > 1
|
.filterNot(LegacyFactionUtils.isFactionLeader(_, gameState))
|
||||||
)
|
.size > 1
|
||||||
) { opmc => fr =>
|
)
|
||||||
opmc.availableDestinationProvinces
|
) { opmc => fr =>
|
||||||
.map(dest => gameState.provinces(dest.provinceId))
|
opmc.availableDestinationProvinces
|
||||||
.filter(_.rulingFactionId.contains(actingFactionId))
|
.map(dest => gameState.provinces(dest.provinceId))
|
||||||
.filter(_.rulingFactionHeroIds.size == 1)
|
.filter(_.rulingFactionId.contains(actingFactionId))
|
||||||
.filter(dest =>
|
.filter(_.rulingFactionHeroIds.size == 1)
|
||||||
LegacyFactionUtils
|
.filter(dest =>
|
||||||
.isFactionLeader(dest.rulingFactionHeroIds.head, gameState)
|
LegacyFactionUtils
|
||||||
)
|
.isFactionLeader(dest.rulingFactionHeroIds.head, gameState)
|
||||||
.map { destination =>
|
)
|
||||||
fr.nextRandomElement(opmc.availableHeroIds)
|
.map { destination =>
|
||||||
.map { hid =>
|
fr.nextRandomElement(opmc.availableHeroIds)
|
||||||
CombatUnit(factionId = actingFactionId, heroId = hid)
|
.map { hid =>
|
||||||
}
|
CombatUnit(factionId = actingFactionId, heroId = hid)
|
||||||
.map { combatUnit =>
|
}
|
||||||
Some(
|
.map { combatUnit =>
|
||||||
CommandSelection(
|
Some(
|
||||||
actingFactionId = actingFactionId,
|
CommandSelection(
|
||||||
actingProvinceId = ac.actingProvinceId,
|
actingFactionId = actingFactionId,
|
||||||
available = ac,
|
actingProvinceId = ac.actingProvinceId,
|
||||||
selected = MarchSelectedCommand(
|
available = ac,
|
||||||
gold = 0,
|
selected = MarchSelectedCommand(
|
||||||
food = 0,
|
gold = 0,
|
||||||
originProvince = opmc.originProvinceId,
|
food = 0,
|
||||||
destinationProvinceId = destination.id,
|
originProvince = opmc.originProvinceId,
|
||||||
marchingUnits = Vector(
|
destinationProvinceId = destination.id,
|
||||||
combatUnit
|
marchingUnits = Vector(
|
||||||
)
|
combatUnit
|
||||||
),
|
)
|
||||||
reason =
|
),
|
||||||
"Moving a hero so that a faction leader won't be alone"
|
reason = "Moving a hero so that a faction leader won't be alone"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
.headOption
|
||||||
.headOption
|
.getOrElse(RandomState(None, fr))
|
||||||
.getOrElse(RandomState(None, fr))
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
package net.eagle0.eagle.ai
|
package net.eagle0.eagle.ai
|
||||||
|
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{AvailableCommand, DiplomacyAvailableCommand}
|
||||||
AvailableCommand,
|
|
||||||
DiplomacyAvailableCommand
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.api.command.util.diplomacy_option.InvitationOption
|
import net.eagle0.eagle.api.command.util.diplomacy_option.InvitationOption
|
||||||
import net.eagle0.eagle.api.selected_command.DiplomacySelectedCommand
|
import net.eagle0.eagle.api.selected_command.DiplomacySelectedCommand
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.settings.MinChanceForAIInvite
|
import net.eagle0.eagle.library.settings.MinChanceForAIInvite
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
import net.eagle0.eagle.library.util.command_choice_helpers.{ProvinceGoldSurplusCalculator, TrustForDiplomacy}
|
||||||
ProvinceGoldSurplusCalculator,
|
|
||||||
TrustForDiplomacy
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
|
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
|
||||||
import net.eagle0.eagle.library.util.CommandSelection
|
import net.eagle0.eagle.library.util.CommandSelection
|
||||||
import net.eagle0.eagle.FactionId
|
import net.eagle0.eagle.FactionId
|
||||||
@@ -27,48 +21,41 @@ object InvitationCommandSelector {
|
|||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
availableCommands: Vector[AvailableCommand]
|
availableCommands: Vector[AvailableCommand]
|
||||||
): Option[CommandSelection] =
|
): Option[CommandSelection] =
|
||||||
flatSelectionForType[DiplomacyAvailableCommand](acs = availableCommands) {
|
flatSelectionForType[DiplomacyAvailableCommand](acs = availableCommands) { diplomacyAvailableCommand =>
|
||||||
diplomacyAvailableCommand =>
|
diplomacyAvailableCommand.options.collect { case io: InvitationOption => io }.filter { invitationOption =>
|
||||||
diplomacyAvailableCommand.options
|
TrustForDiplomacy.meetsConditionsForInvitation(
|
||||||
.collect { case io: InvitationOption => io }
|
actingFactionId = actingFactionId,
|
||||||
.filter { invitationOption =>
|
targetFactionId = invitationOption.targetFactionId,
|
||||||
TrustForDiplomacy.meetsConditionsForInvitation(
|
gameState = gameState
|
||||||
actingFactionId = actingFactionId,
|
)
|
||||||
targetFactionId = invitationOption.targetFactionId,
|
}.filter { invitationOption =>
|
||||||
gameState = gameState
|
ProvinceGoldSurplusCalculator.provinceGoldSurplus(
|
||||||
)
|
diplomacyAvailableCommand.actingProvinceId,
|
||||||
}
|
gameState
|
||||||
.filter { invitationOption =>
|
) >= invitationOption.goldCost
|
||||||
ProvinceGoldSurplusCalculator.provinceGoldSurplus(
|
}.map { invitationOption =>
|
||||||
diplomacyAvailableCommand.actingProvinceId,
|
InvitationOptionWithChance(
|
||||||
gameState
|
invitationOption = invitationOption,
|
||||||
) >= invitationOption.goldCost
|
acceptanceChance = ResolveDiplomacyCommandSelector.invitationAcceptanceChance(
|
||||||
}
|
actingFactionId,
|
||||||
.map { invitationOption =>
|
invitationOption.targetFactionId,
|
||||||
InvitationOptionWithChance(
|
gameState
|
||||||
invitationOption = invitationOption,
|
)
|
||||||
acceptanceChance =
|
)
|
||||||
ResolveDiplomacyCommandSelector.invitationAcceptanceChance(
|
}.maxByOption(_.acceptanceChance)
|
||||||
actingFactionId,
|
.filter(_.acceptanceChance >= MinChanceForAIInvite.intValue)
|
||||||
invitationOption.targetFactionId,
|
.map { iowc =>
|
||||||
gameState
|
CommandSelection(
|
||||||
)
|
actingFactionId = actingFactionId,
|
||||||
)
|
actingProvinceId = diplomacyAvailableCommand.actingProvinceId,
|
||||||
}
|
available = diplomacyAvailableCommand,
|
||||||
.maxByOption { _.acceptanceChance }
|
selected = DiplomacySelectedCommand(
|
||||||
.filter(_.acceptanceChance >= MinChanceForAIInvite.intValue)
|
selectedOption = iowc.invitationOption,
|
||||||
.map { iowc =>
|
targetFactionId = iowc.invitationOption.targetFactionId,
|
||||||
CommandSelection(
|
sentHeroId = diplomacyAvailableCommand.recommendedHeroId
|
||||||
actingFactionId = actingFactionId,
|
),
|
||||||
actingProvinceId = diplomacyAvailableCommand.actingProvinceId,
|
reason = "maybeInviteOtherFaction"
|
||||||
available = diplomacyAvailableCommand,
|
)
|
||||||
selected = DiplomacySelectedCommand(
|
}
|
||||||
selectedOption = iowc.invitationOption,
|
|
||||||
targetFactionId = iowc.invitationOption.targetFactionId,
|
|
||||||
sentHeroId = diplomacyAvailableCommand.recommendedHeroId
|
|
||||||
),
|
|
||||||
reason = "maybeInviteOtherFaction"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
package net.eagle0.eagle.ai
|
package net.eagle0.eagle.ai
|
||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{MarchAvailableCommand, MarchCommandFromOneProvince}
|
||||||
MarchAvailableCommand,
|
|
||||||
MarchCommandFromOneProvince
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
||||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.util.{
|
import net.eagle0.eagle.library.util.{CommandSelection, LegacyBattalionUtils, ProvinceDistances}
|
||||||
CommandSelection,
|
|
||||||
LegacyBattalionUtils,
|
|
||||||
ProvinceDistances
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.ProvinceGoldSurplusCalculator.provinceGoldSurplus
|
import net.eagle0.eagle.library.util.command_choice_helpers.ProvinceGoldSurplusCalculator.provinceGoldSurplus
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||||
|
|
||||||
@@ -28,79 +21,80 @@ object MarchTowardProvinceCommandChooser {
|
|||||||
val bestCommand =
|
val bestCommand =
|
||||||
opmc.availableDestinationProvinces
|
opmc.availableDestinationProvinces
|
||||||
.map(dest => (opmc, gs.provinces(dest.provinceId)))
|
.map(dest => (opmc, gs.provinces(dest.provinceId)))
|
||||||
.flatMap { case (opmc, p) =>
|
.flatMap {
|
||||||
ProvinceDistances
|
case (opmc, p) =>
|
||||||
.distanceThroughFriendliesOption(
|
ProvinceDistances
|
||||||
destinationProvinceId,
|
.distanceThroughFriendliesOption(
|
||||||
p.id,
|
destinationProvinceId,
|
||||||
fid,
|
p.id,
|
||||||
gs
|
fid,
|
||||||
)
|
gs
|
||||||
.map(distance => (opmc, p, distance))
|
)
|
||||||
|
.map(distance => (opmc, p, distance))
|
||||||
}
|
}
|
||||||
.minByOption(_._3)
|
.minByOption(_._3)
|
||||||
|
|
||||||
val actingProvinceId = ac.actingProvinceId
|
val actingProvinceId = ac.actingProvinceId
|
||||||
|
|
||||||
bestCommand.flatMap { case (opmc, dest, _) =>
|
bestCommand.flatMap {
|
||||||
internalRequire(
|
case (opmc, dest, _) =>
|
||||||
opmc.originProvinceId != destinationProvinceId,
|
internalRequire(
|
||||||
s"Marching away from $destinationProvinceId in an effort to get to that province"
|
opmc.originProvinceId != destinationProvinceId,
|
||||||
)
|
s"Marching away from $destinationProvinceId in an effort to get to that province"
|
||||||
|
|
||||||
val originProvince = gs.provinces(opmc.originProvinceId)
|
|
||||||
|
|
||||||
val takenHeroIds =
|
|
||||||
AIClientUtils.takenHeroIdsForMarchTowardFocus(
|
|
||||||
originProvince = originProvince,
|
|
||||||
destinationProvince = dest,
|
|
||||||
availableHeroIds = opmc.availableHeroIds.toVector,
|
|
||||||
favorLeaders = favorLeaders,
|
|
||||||
gs = gs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Option.when(takenHeroIds.nonEmpty) {
|
val originProvince = gs.provinces(opmc.originProvinceId)
|
||||||
val takenBattalions =
|
|
||||||
if originProvince.battalionIds.size <= takenHeroIds.size then
|
|
||||||
originProvince.battalionIds.map(gs.battalions)
|
|
||||||
else
|
|
||||||
originProvince.battalionIds
|
|
||||||
.map(gs.battalions)
|
|
||||||
.sortBy(_.size)
|
|
||||||
.take(takenHeroIds.size)
|
|
||||||
|
|
||||||
val takenUnits = takenHeroIds
|
val takenHeroIds =
|
||||||
.zipAll(
|
AIClientUtils.takenHeroIdsForMarchTowardFocus(
|
||||||
takenBattalions.map(batt => Some(batt.id)),
|
originProvince = originProvince,
|
||||||
0,
|
destinationProvince = dest,
|
||||||
None
|
availableHeroIds = opmc.availableHeroIds.toVector,
|
||||||
|
favorLeaders = favorLeaders,
|
||||||
|
gs = gs
|
||||||
)
|
)
|
||||||
.map { case (hid, bid) =>
|
|
||||||
CombatUnit(factionId = fid, heroId = hid, battalionId = bid)
|
|
||||||
}
|
|
||||||
|
|
||||||
val foodToTake = 2 * LegacyBattalionUtils.monthlyConsumedFood(
|
Option.when(takenHeroIds.nonEmpty) {
|
||||||
takenBattalions,
|
val takenBattalions =
|
||||||
gs.battalionTypes.toVector
|
if originProvince.battalionIds.size <= takenHeroIds.size then originProvince.battalionIds.map(gs.battalions)
|
||||||
)
|
else
|
||||||
val goldToTake = provinceGoldSurplus(originProvince.id, gs)
|
originProvince.battalionIds
|
||||||
CommandSelection(
|
.map(gs.battalions)
|
||||||
actingFactionId = fid,
|
.sortBy(_.size)
|
||||||
actingProvinceId = actingProvinceId,
|
.take(takenHeroIds.size)
|
||||||
available = ac,
|
|
||||||
selected = MarchSelectedCommand(
|
val takenUnits = takenHeroIds
|
||||||
marchingUnits = takenUnits, // Check the food cost here?
|
.zipAll(
|
||||||
destinationProvinceId = dest.id,
|
takenBattalions.map(batt => Some(batt.id)),
|
||||||
originProvince = opmc.originProvinceId,
|
0,
|
||||||
gold = Math.min(goldToTake, opmc.goldAvailable),
|
None
|
||||||
food = Math.min(foodToTake, opmc.foodAvailable)
|
)
|
||||||
),
|
.map {
|
||||||
reason = if favorLeaders then
|
case (hid, bid) =>
|
||||||
s"marching leaders toward ${gs.provinces(destinationProvinceId).name}"
|
CombatUnit(factionId = fid, heroId = hid, battalionId = bid)
|
||||||
else
|
}
|
||||||
s"marching heroes toward ${gs.provinces(destinationProvinceId).name}"
|
|
||||||
)
|
val foodToTake = 2 * LegacyBattalionUtils.monthlyConsumedFood(
|
||||||
}
|
takenBattalions,
|
||||||
|
gs.battalionTypes.toVector
|
||||||
|
)
|
||||||
|
val goldToTake = provinceGoldSurplus(originProvince.id, gs)
|
||||||
|
CommandSelection(
|
||||||
|
actingFactionId = fid,
|
||||||
|
actingProvinceId = actingProvinceId,
|
||||||
|
available = ac,
|
||||||
|
selected = MarchSelectedCommand(
|
||||||
|
marchingUnits = takenUnits, // Check the food cost here?
|
||||||
|
destinationProvinceId = dest.id,
|
||||||
|
originProvince = opmc.originProvinceId,
|
||||||
|
gold = Math.min(goldToTake, opmc.goldAvailable),
|
||||||
|
food = Math.min(foodToTake, opmc.foodAvailable)
|
||||||
|
),
|
||||||
|
reason =
|
||||||
|
if favorLeaders then s"marching leaders toward ${gs.provinces(destinationProvinceId).name}"
|
||||||
|
else s"marching heroes toward ${gs.provinces(destinationProvinceId).name}"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,13 @@ import net.eagle0.eagle.{FactionId, ProvinceId}
|
|||||||
import net.eagle0.eagle.ai.AIClientUtils.extraHeroCount
|
import net.eagle0.eagle.ai.AIClientUtils.extraHeroCount
|
||||||
import net.eagle0.eagle.api.available_command.*
|
import net.eagle0.eagle.api.available_command.*
|
||||||
import net.eagle0.eagle.api.command.OneProvinceAvailableCommands
|
import net.eagle0.eagle.api.command.OneProvinceAvailableCommands
|
||||||
import net.eagle0.eagle.api.selected_command.{
|
import net.eagle0.eagle.api.selected_command.{MarchSelectedCommand, ReconSelectedCommand}
|
||||||
MarchSelectedCommand,
|
|
||||||
ReconSelectedCommand
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||||
import net.eagle0.eagle.common.improvement_type.ImprovementType
|
import net.eagle0.eagle.common.improvement_type.ImprovementType
|
||||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.province.Province
|
import net.eagle0.eagle.internal.province.Province
|
||||||
import net.eagle0.eagle.library.settings.{
|
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, MonthsReconConsideredRecent}
|
||||||
MinSupportForTaxes,
|
|
||||||
MonthsReconConsideredRecent
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.*
|
import net.eagle0.eagle.library.util.*
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
||||||
AlmsCommandSelector,
|
AlmsCommandSelector,
|
||||||
@@ -141,20 +135,20 @@ object MidGameAIClient {
|
|||||||
_ /* availableHeroIds */,
|
_ /* availableHeroIds */,
|
||||||
_ /* unknownFieldSet */
|
_ /* unknownFieldSet */
|
||||||
) =>
|
) =>
|
||||||
val actingProvince = gameState.provinces(actingProvinceId)
|
val actingProvince = gameState.provinces(actingProvinceId)
|
||||||
val foodMonthsToHoldBack =
|
val foodMonthsToHoldBack =
|
||||||
FoodConsumptionUtils.foodConsumptionMonthsToHold(
|
FoodConsumptionUtils.foodConsumptionMonthsToHold(
|
||||||
actingProvinceId,
|
actingProvinceId,
|
||||||
gameState
|
gameState
|
||||||
)
|
)
|
||||||
val foodToHoldBack =
|
val foodToHoldBack =
|
||||||
foodMonthsToHoldBack * LegacyProvinceUtils.monthlyFoodConsumption(
|
foodMonthsToHoldBack * LegacyProvinceUtils.monthlyFoodConsumption(
|
||||||
actingProvinceId,
|
actingProvinceId,
|
||||||
gameState
|
gameState
|
||||||
)
|
)
|
||||||
val maxFoodToSend =
|
val maxFoodToSend =
|
||||||
Math.min(actingProvince.food - foodToHoldBack, foodAvailable)
|
Math.min(actingProvince.food - foodToHoldBack, foodAvailable)
|
||||||
val maxGoldToSend = Math.min(
|
val maxGoldToSend = Math.min(
|
||||||
ProvinceGoldSurplusCalculator
|
ProvinceGoldSurplusCalculator
|
||||||
.provinceGoldSurplus(actingProvinceId, gameState),
|
.provinceGoldSurplus(actingProvinceId, gameState),
|
||||||
goldAvailable
|
goldAvailable
|
||||||
@@ -172,12 +166,12 @@ object MidGameAIClient {
|
|||||||
.map(gameState.provinces)
|
.map(gameState.provinces)
|
||||||
.filter(_.rulingFactionId.contains(factionId))
|
.filter(_.rulingFactionId.contains(factionId))
|
||||||
.map { province =>
|
.map { province =>
|
||||||
val consumption = LegacyProvinceUtils
|
val consumption = LegacyProvinceUtils
|
||||||
.monthlyFoodConsumption(province.id, gameState)
|
.monthlyFoodConsumption(province.id, gameState)
|
||||||
val incomingFood =
|
val incomingFood =
|
||||||
province.incomingShipments.flatMap(_.supplies).map(_.food).sum
|
province.incomingShipments.flatMap(_.supplies).map(_.food).sum
|
||||||
val availableFood = province.food + incomingFood
|
val availableFood = province.food + incomingFood
|
||||||
val monthsOfFood =
|
val monthsOfFood =
|
||||||
if consumption == 0 then 120
|
if consumption == 0 then 120
|
||||||
else availableFood / consumption
|
else availableFood / consumption
|
||||||
DestinationFoodStatus(
|
DestinationFoodStatus(
|
||||||
@@ -189,29 +183,28 @@ object MidGameAIClient {
|
|||||||
.filter(_.monthsOfFood < 4)
|
.filter(_.monthsOfFood < 4)
|
||||||
.minByOption(_.monthsOfFood)
|
.minByOption(_.monthsOfFood)
|
||||||
|
|
||||||
bestDestination
|
bestDestination.map { destinationFoodStatus =>
|
||||||
.map { destinationFoodStatus =>
|
val destinationProvince = destinationFoodStatus.province
|
||||||
val destinationProvince = destinationFoodStatus.province
|
val desiredFood = LegacyProvinceUtils.monthlyFoodConsumption(
|
||||||
val desiredFood = LegacyProvinceUtils.monthlyFoodConsumption(
|
destinationProvince.id,
|
||||||
destinationProvince.id,
|
gameState
|
||||||
gameState
|
) * 6 - destinationProvince.food
|
||||||
) * 6 - destinationProvince.food
|
val foodToSend =
|
||||||
val foodToSend =
|
if desiredFood > maxFoodToSend then maxFoodToSend
|
||||||
if desiredFood > maxFoodToSend then maxFoodToSend
|
else (desiredFood + maxFoodToSend) / 2
|
||||||
else (desiredFood + maxFoodToSend) / 2
|
|
||||||
|
|
||||||
CommandChoiceHelpers
|
CommandChoiceHelpers
|
||||||
.chosenSendSuppliesCommandWithSuppliesAndDestination(
|
.chosenSendSuppliesCommandWithSuppliesAndDestination(
|
||||||
actingFactionId = factionId,
|
actingFactionId = factionId,
|
||||||
gameState = gameState,
|
gameState = gameState,
|
||||||
ac = available,
|
ac = available,
|
||||||
supplies = Supplies(
|
supplies = Supplies(
|
||||||
gold = maxGoldToSend / 2,
|
gold = maxGoldToSend / 2,
|
||||||
food = foodToSend
|
food = foodToSend
|
||||||
),
|
),
|
||||||
destination = destinationFoodStatus.province.id
|
destination = destinationFoodStatus.province.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.map(_.withReason("relay supplies"))
|
.map(_.withReason("relay supplies"))
|
||||||
@@ -335,71 +328,70 @@ object MidGameAIClient {
|
|||||||
availableCommands: Vector[AvailableCommand],
|
availableCommands: Vector[AvailableCommand],
|
||||||
functionalRandom: FunctionalRandom
|
functionalRandom: FunctionalRandom
|
||||||
): RandomState[Option[CommandSelection]] =
|
): RandomState[Option[CommandSelection]] =
|
||||||
provincesWithFactionLeader(actingFactionId, gameState)
|
provincesWithFactionLeader(actingFactionId, gameState).map { province =>
|
||||||
.map { province =>
|
MoreOption
|
||||||
MoreOption
|
.flatWhen(province.rulingFactionHeroIds.size < 4)(
|
||||||
.flatWhen(province.rulingFactionHeroIds.size < 4)(
|
maybeMoveHeroesTowardFactionLeaderCommand(
|
||||||
maybeMoveHeroesTowardFactionLeaderCommand(
|
actingFactionId = actingFactionId,
|
||||||
actingFactionId = actingFactionId,
|
gs = gameState,
|
||||||
gs = gameState,
|
acs = availableCommands
|
||||||
acs = availableCommands
|
)
|
||||||
)
|
) match {
|
||||||
) match {
|
case Some(cs) => RandomState(Some(cs), functionalRandom)
|
||||||
case Some(cs) => RandomState(Some(cs), functionalRandom)
|
case None =>
|
||||||
case None =>
|
chosenGenericCommandFromRankedOptions(
|
||||||
chosenGenericCommandFromRankedOptions(
|
actingFactionId = actingFactionId,
|
||||||
actingFactionId = actingFactionId,
|
gs = gameState,
|
||||||
gs = gameState,
|
acs = availableCommands,
|
||||||
acs = availableCommands,
|
choosersAndReasons = Vector(
|
||||||
choosersAndReasons = Vector(
|
(
|
||||||
(
|
ExpandCommandSelector.maybeChosenExpandCommand,
|
||||||
ExpandCommandSelector.maybeChosenExpandCommand,
|
"no hostile neighbors, expand"
|
||||||
"no hostile neighbors, expand"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
maybeMoveToRecruitCommand,
|
|
||||||
"no hostile neighbors, travel to recruit"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
(
|
|
||||||
actingFactionId,
|
|
||||||
gameState,
|
|
||||||
availableCommands,
|
|
||||||
functionalRandom
|
|
||||||
) =>
|
|
||||||
RandomState(
|
|
||||||
ImproveCommandSelector.chosenImproveCommand(
|
|
||||||
actingFactionId,
|
|
||||||
gameState,
|
|
||||||
availableCommands
|
|
||||||
),
|
|
||||||
functionalRandom
|
|
||||||
),
|
|
||||||
"chosen neutral neighbors: Improve b/c nothing better"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
(
|
|
||||||
actingFactionId,
|
|
||||||
gameState,
|
|
||||||
availableCommands,
|
|
||||||
functionalRandom
|
|
||||||
) =>
|
|
||||||
RandomState(
|
|
||||||
chosenRestCommand(
|
|
||||||
actingFactionId,
|
|
||||||
gameState,
|
|
||||||
availableCommands,
|
|
||||||
"chosen neutral neighbors: rest"
|
|
||||||
),
|
|
||||||
functionalRandom
|
|
||||||
),
|
|
||||||
"chosen neutral neighbors: rest"
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
functionalRandom = functionalRandom
|
(
|
||||||
)
|
maybeMoveToRecruitCommand,
|
||||||
}
|
"no hostile neighbors, travel to recruit"
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(
|
||||||
|
actingFactionId,
|
||||||
|
gameState,
|
||||||
|
availableCommands,
|
||||||
|
functionalRandom
|
||||||
|
) =>
|
||||||
|
RandomState(
|
||||||
|
ImproveCommandSelector.chosenImproveCommand(
|
||||||
|
actingFactionId,
|
||||||
|
gameState,
|
||||||
|
availableCommands
|
||||||
|
),
|
||||||
|
functionalRandom
|
||||||
|
),
|
||||||
|
"chosen neutral neighbors: Improve b/c nothing better"
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(
|
||||||
|
actingFactionId,
|
||||||
|
gameState,
|
||||||
|
availableCommands,
|
||||||
|
functionalRandom
|
||||||
|
) =>
|
||||||
|
RandomState(
|
||||||
|
chosenRestCommand(
|
||||||
|
actingFactionId,
|
||||||
|
gameState,
|
||||||
|
availableCommands,
|
||||||
|
"chosen neutral neighbors: rest"
|
||||||
|
),
|
||||||
|
functionalRandom
|
||||||
|
),
|
||||||
|
"chosen neutral neighbors: rest"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
functionalRandom = functionalRandom
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.find(_.newValue.isDefined)
|
.find(_.newValue.isDefined)
|
||||||
.get
|
.get
|
||||||
|
|
||||||
@@ -410,85 +402,84 @@ object MidGameAIClient {
|
|||||||
functionalRandom: FunctionalRandom
|
functionalRandom: FunctionalRandom
|
||||||
): RandomState[Option[CommandSelection]] = {
|
): RandomState[Option[CommandSelection]] = {
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooserImplicits.DeterministicCommandChooser
|
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooserImplicits.DeterministicCommandChooser
|
||||||
provincesWithFactionLeader(actingFactionId, gameState)
|
provincesWithFactionLeader(actingFactionId, gameState).map { province =>
|
||||||
.map { province =>
|
MoreOption
|
||||||
MoreOption
|
.flatWhen(province.rulingFactionHeroIds.size < 4)(
|
||||||
.flatWhen(province.rulingFactionHeroIds.size < 4)(
|
maybeMoveHeroesTowardFactionLeaderCommand(
|
||||||
maybeMoveHeroesTowardFactionLeaderCommand(
|
actingFactionId = actingFactionId,
|
||||||
actingFactionId = actingFactionId,
|
gs = gameState,
|
||||||
gs = gameState,
|
acs = availableCommands
|
||||||
acs = availableCommands
|
)
|
||||||
)
|
) match {
|
||||||
) match {
|
case Some(cs) => RandomState(Some(cs), functionalRandom)
|
||||||
case Some(cs) => RandomState(Some(cs), functionalRandom)
|
case None =>
|
||||||
case None =>
|
chosenGenericCommandFromRankedOptions(
|
||||||
chosenGenericCommandFromRankedOptions(
|
actingFactionId = actingFactionId,
|
||||||
actingFactionId = actingFactionId,
|
gs = gameState,
|
||||||
gs = gameState,
|
acs = availableCommands,
|
||||||
acs = availableCommands,
|
choosersAndReasons = Vector(
|
||||||
choosersAndReasons = Vector(
|
(
|
||||||
|
maybeMoveToRecruitCommand,
|
||||||
|
"chosen no neutral neighbors, travel to recruit"
|
||||||
|
),
|
||||||
|
(
|
||||||
(
|
(
|
||||||
maybeMoveToRecruitCommand,
|
actingFactionId,
|
||||||
"chosen no neutral neighbors, travel to recruit"
|
gameState,
|
||||||
),
|
availableCommands,
|
||||||
(
|
functionalRandom
|
||||||
(
|
) =>
|
||||||
actingFactionId,
|
RandomState(
|
||||||
gameState,
|
MoveLeaderToBetterProvinceCommandChooser
|
||||||
availableCommands,
|
.maybeMoveLeaderToBetterProvinceCommand(
|
||||||
functionalRandom
|
|
||||||
) =>
|
|
||||||
RandomState(
|
|
||||||
MoveLeaderToBetterProvinceCommandChooser
|
|
||||||
.maybeMoveLeaderToBetterProvinceCommand(
|
|
||||||
actingFactionId,
|
|
||||||
gameState,
|
|
||||||
availableCommands
|
|
||||||
),
|
|
||||||
functionalRandom
|
|
||||||
),
|
|
||||||
"chosen no neutral neighbors, travel to front"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
(
|
|
||||||
actingFactionId,
|
|
||||||
gameState,
|
|
||||||
availableCommands,
|
|
||||||
functionalRandom
|
|
||||||
) =>
|
|
||||||
RandomState(
|
|
||||||
ImproveCommandSelector.chosenImproveCommand(
|
|
||||||
actingFactionId,
|
actingFactionId,
|
||||||
gameState,
|
gameState,
|
||||||
availableCommands
|
availableCommands
|
||||||
),
|
),
|
||||||
functionalRandom
|
functionalRandom
|
||||||
),
|
),
|
||||||
"chosen no neutral neighbors: Improve b/c nothing better"
|
"chosen no neutral neighbors, travel to front"
|
||||||
),
|
),
|
||||||
|
(
|
||||||
(
|
(
|
||||||
(
|
actingFactionId,
|
||||||
|
gameState,
|
||||||
|
availableCommands,
|
||||||
|
functionalRandom
|
||||||
|
) =>
|
||||||
|
RandomState(
|
||||||
|
ImproveCommandSelector.chosenImproveCommand(
|
||||||
|
actingFactionId,
|
||||||
|
gameState,
|
||||||
|
availableCommands
|
||||||
|
),
|
||||||
|
functionalRandom
|
||||||
|
),
|
||||||
|
"chosen no neutral neighbors: Improve b/c nothing better"
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(
|
||||||
|
actingFactionId,
|
||||||
|
gameState,
|
||||||
|
availableCommands,
|
||||||
|
functionalRandom
|
||||||
|
) =>
|
||||||
|
RandomState(
|
||||||
|
chosenRestCommand(
|
||||||
actingFactionId,
|
actingFactionId,
|
||||||
gameState,
|
gameState,
|
||||||
availableCommands,
|
availableCommands,
|
||||||
functionalRandom
|
"chosen no neutral neighbors: rest"
|
||||||
) =>
|
|
||||||
RandomState(
|
|
||||||
chosenRestCommand(
|
|
||||||
actingFactionId,
|
|
||||||
gameState,
|
|
||||||
availableCommands,
|
|
||||||
"chosen no neutral neighbors: rest"
|
|
||||||
),
|
|
||||||
functionalRandom
|
|
||||||
),
|
),
|
||||||
"chosen neutral neighbors: rest"
|
functionalRandom
|
||||||
)
|
),
|
||||||
),
|
"chosen neutral neighbors: rest"
|
||||||
functionalRandom = functionalRandom
|
)
|
||||||
)
|
),
|
||||||
}
|
functionalRandom = functionalRandom
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.find(_.newValue.isDefined)
|
.find(_.newValue.isDefined)
|
||||||
.get
|
.get
|
||||||
}
|
}
|
||||||
@@ -503,11 +494,11 @@ object MidGameAIClient {
|
|||||||
.reconnedProvinces
|
.reconnedProvinces
|
||||||
.find(_.id == provinceId)
|
.find(_.id == provinceId)
|
||||||
.flatMap(_.fullInfo)
|
.flatMap(_.fullInfo)
|
||||||
def reconnedHeroCountFor(provinceId: ProvinceId): Int =
|
def reconnedHeroCountFor(provinceId: ProvinceId): Int =
|
||||||
reconnedFullInfoFor(provinceId)
|
reconnedFullInfoFor(provinceId)
|
||||||
.map(_.rulingFactionHeroIds.size)
|
.map(_.rulingFactionHeroIds.size)
|
||||||
.getOrElse(0)
|
.getOrElse(0)
|
||||||
def reconnedBattalionsFor(provinceId: ProvinceId): Vector[BattalionView] =
|
def reconnedBattalionsFor(provinceId: ProvinceId): Vector[BattalionView] =
|
||||||
reconnedFullInfoFor(provinceId)
|
reconnedFullInfoFor(provinceId)
|
||||||
.map(_.battalions.toVector)
|
.map(_.battalions.toVector)
|
||||||
.getOrElse(Vector())
|
.getOrElse(Vector())
|
||||||
@@ -673,8 +664,7 @@ object MidGameAIClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
(fid, gs, acs, fr) =>
|
(fid, gs, acs, fr) => chosenNoNeutralNeighborsCommand(fid, gs, acs, fr)
|
||||||
chosenNoNeutralNeighborsCommand(fid, gs, acs, fr)
|
|
||||||
),
|
),
|
||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
@@ -696,7 +686,7 @@ object MidGameAIClient {
|
|||||||
factionId: FactionId
|
factionId: FactionId
|
||||||
): Option[CommandSelection] =
|
): Option[CommandSelection] =
|
||||||
flatSelectionForType[ImproveAvailableCommand](acs) { availableCommand =>
|
flatSelectionForType[ImproveAvailableCommand](acs) { availableCommand =>
|
||||||
val province = gs.provinces(availableCommand.actingProvinceId)
|
val province = gs.provinces(availableCommand.actingProvinceId)
|
||||||
val existingBattalions = province.battalionIds
|
val existingBattalions = province.battalionIds
|
||||||
.map(gs.battalions)
|
.map(gs.battalions)
|
||||||
|
|
||||||
@@ -725,7 +715,7 @@ object MidGameAIClient {
|
|||||||
.map(_.minimumAgriculture - province.agriculture)
|
.map(_.minimumAgriculture - province.agriculture)
|
||||||
.filter(_ > 0)
|
.filter(_ > 0)
|
||||||
.min
|
.min
|
||||||
val neededEconomy = notAvailable
|
val neededEconomy = notAvailable
|
||||||
.map(_.minimumEconomy - province.economy)
|
.map(_.minimumEconomy - province.economy)
|
||||||
.filter(_ > 0)
|
.filter(_ > 0)
|
||||||
.min
|
.min
|
||||||
@@ -870,7 +860,7 @@ object MidGameAIClient {
|
|||||||
case (None, fr2) => go(RandomState(tail, fr2))
|
case (None, fr2) => go(RandomState(tail, fr2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case (_, fr) => RandomState(None, fr)
|
case (_, fr) => RandomState(None, fr)
|
||||||
}
|
}
|
||||||
|
|
||||||
go(
|
go(
|
||||||
@@ -889,35 +879,34 @@ object MidGameAIClient {
|
|||||||
val factionLeaderProvincesNeedingHeroes =
|
val factionLeaderProvincesNeedingHeroes =
|
||||||
provincesWithFactionLeader(actingFactionId, gs)
|
provincesWithFactionLeader(actingFactionId, gs)
|
||||||
.filter(p => p.rulingFactionHeroIds.size < p.heroCap)
|
.filter(p => p.rulingFactionHeroIds.size < p.heroCap)
|
||||||
val cs = for {
|
val cs = for {
|
||||||
(marchCommand, idx) <- acs.zipWithIndex.collectFirst {
|
(marchCommand, idx) <- acs.zipWithIndex.collectFirst {
|
||||||
case (ac: MarchAvailableCommand, idx) => (ac, idx)
|
case (ac: MarchAvailableCommand, idx) => (ac, idx)
|
||||||
}
|
}
|
||||||
} yield {
|
} yield {
|
||||||
// Find the march command origin provinces that contain extra heroes
|
// Find the march command origin provinces that contain extra heroes
|
||||||
val candidateCommands = marchCommand.oneProvinceCommands.filter { opmc =>
|
val candidateCommands = marchCommand.oneProvinceCommands.filter { opmc =>
|
||||||
extraHeroCount(opmc.originProvinceId, actingFactionId, gs) > 0
|
extraHeroCount(opmc.originProvinceId, actingFactionId, gs) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val furthestOrigin = candidateCommands
|
val furthestOrigin = candidateCommands.flatMap { opmc =>
|
||||||
.flatMap { opmc =>
|
// distance to the faction leader province we're closest to
|
||||||
// distance to the faction leader province we're closest to
|
factionLeaderProvincesNeedingHeroes.flatMap { flp =>
|
||||||
factionLeaderProvincesNeedingHeroes
|
ProvinceDistances
|
||||||
.flatMap { flp =>
|
.distanceThroughFriendliesOption(
|
||||||
ProvinceDistances
|
p1 = flp.id,
|
||||||
.distanceThroughFriendliesOption(
|
p2 = opmc.originProvinceId,
|
||||||
p1 = flp.id,
|
fid = actingFactionId,
|
||||||
p2 = opmc.originProvinceId,
|
gs = gs
|
||||||
fid = actingFactionId,
|
)
|
||||||
gs = gs
|
.map {
|
||||||
)
|
(_: Int, flp.id: ProvinceId)
|
||||||
.map {
|
|
||||||
(_: Int, flp.id: ProvinceId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.minByOption(_._1)
|
}
|
||||||
.filterNot(_._1 == 0) // If distance is zero, we'd be moving away from the province we're trying to get to
|
.minByOption(_._1)
|
||||||
.map { case (distance: Int, destinationProvinceId: ProvinceId) =>
|
.filterNot(_._1 == 0) // If distance is zero, we'd be moving away from the province we're trying to get to
|
||||||
|
.map {
|
||||||
|
case (distance: Int, destinationProvinceId: ProvinceId) =>
|
||||||
(
|
(
|
||||||
// distance to the faction leader province we're closest to
|
// distance to the faction leader province we're closest to
|
||||||
distance,
|
distance,
|
||||||
@@ -925,21 +914,22 @@ object MidGameAIClient {
|
|||||||
extraHeroCount(opmc.originProvinceId, actingFactionId, gs),
|
extraHeroCount(opmc.originProvinceId, actingFactionId, gs),
|
||||||
opmc
|
opmc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.maxByOption(tup => (tup._1, tup._3))
|
.maxByOption(tup => (tup._1, tup._3))
|
||||||
.map { tup => (tup._2, tup._4) }
|
.map(tup => (tup._2, tup._4))
|
||||||
|
|
||||||
furthestOrigin.flatMap { case (destinationProvinceId, opmc) =>
|
furthestOrigin.flatMap {
|
||||||
MarchTowardProvinceCommandChooser
|
case (destinationProvinceId, opmc) =>
|
||||||
.marchTowardProvinceCommand(
|
MarchTowardProvinceCommandChooser
|
||||||
ac = marchCommand,
|
.marchTowardProvinceCommand(
|
||||||
fid = actingFactionId,
|
ac = marchCommand,
|
||||||
opmc = opmc,
|
fid = actingFactionId,
|
||||||
destinationProvinceId = destinationProvinceId,
|
opmc = opmc,
|
||||||
gs = gs,
|
destinationProvinceId = destinationProvinceId,
|
||||||
favorLeaders = false
|
gs = gs,
|
||||||
)
|
favorLeaders = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -957,8 +947,7 @@ object MidGameAIClient {
|
|||||||
actingProvinceId = ac.actingProvinceId,
|
actingProvinceId = ac.actingProvinceId,
|
||||||
available = ac,
|
available = ac,
|
||||||
selected = ReconSelectedCommand(
|
selected = ReconSelectedCommand(
|
||||||
actingHeroId =
|
actingHeroId = ac.availableHeroIds.maxBy(hid => gameState.heroes(hid).vigor),
|
||||||
ac.availableHeroIds.maxBy(hid => gameState.heroes(hid).vigor),
|
|
||||||
targetProvinceId = targetProvinceId
|
targetProvinceId = targetProvinceId
|
||||||
),
|
),
|
||||||
reason = "selectedReconCommand"
|
reason = "selectedReconCommand"
|
||||||
@@ -1046,11 +1035,11 @@ object MidGameAIClient {
|
|||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
acs: Vector[AvailableCommand]
|
acs: Vector[AvailableCommand]
|
||||||
): Option[CommandSelection] = {
|
): Option[CommandSelection] = {
|
||||||
val faction = gameState.factions(actingFactionId)
|
val faction = gameState.factions(actingFactionId)
|
||||||
val factionLeaders = faction.leaders
|
val factionLeaders = faction.leaders
|
||||||
flatSelectionForType[MarchAvailableCommand](acs) { marchAvailableCommand =>
|
flatSelectionForType[MarchAvailableCommand](acs) { marchAvailableCommand =>
|
||||||
marchAvailableCommand.oneProvinceCommands.flatMap { opmc =>
|
marchAvailableCommand.oneProvinceCommands.flatMap { opmc =>
|
||||||
val originProvince = gameState.provinces(opmc.originProvinceId)
|
val originProvince = gameState.provinces(opmc.originProvinceId)
|
||||||
val leadersInOriginProvince =
|
val leadersInOriginProvince =
|
||||||
originProvince.rulingFactionHeroIds.intersect(factionLeaders)
|
originProvince.rulingFactionHeroIds.intersect(factionLeaders)
|
||||||
MoreOption.flatWhen(
|
MoreOption.flatWhen(
|
||||||
@@ -1058,7 +1047,7 @@ object MidGameAIClient {
|
|||||||
.intersect(factionLeaders)
|
.intersect(factionLeaders)
|
||||||
.nonEmpty
|
.nonEmpty
|
||||||
) {
|
) {
|
||||||
val leaderIdToMove =
|
val leaderIdToMove =
|
||||||
opmc.availableHeroIds
|
opmc.availableHeroIds
|
||||||
.intersect(factionLeaders)
|
.intersect(factionLeaders)
|
||||||
.maxBy(hid => LegacyHeroUtils.seniorityOrder(faction, hid))
|
.maxBy(hid => LegacyHeroUtils.seniorityOrder(faction, hid))
|
||||||
@@ -1074,27 +1063,26 @@ object MidGameAIClient {
|
|||||||
IncomingArmyUtils.isUnderAttack(province, gameState)
|
IncomingArmyUtils.isUnderAttack(province, gameState)
|
||||||
) // don't send a leader into a province under attack
|
) // don't send a leader into a province under attack
|
||||||
|
|
||||||
validDestinations.minByOption(_.rulingFactionHeroIds.size).map {
|
validDestinations.minByOption(_.rulingFactionHeroIds.size).map { destinationProvince =>
|
||||||
destinationProvince =>
|
CommandSelection(
|
||||||
CommandSelection(
|
actingFactionId = actingFactionId,
|
||||||
actingFactionId = actingFactionId,
|
actingProvinceId = marchAvailableCommand.actingProvinceId,
|
||||||
actingProvinceId = marchAvailableCommand.actingProvinceId,
|
available = marchAvailableCommand,
|
||||||
available = marchAvailableCommand,
|
selected = MarchSelectedCommand(
|
||||||
selected = MarchSelectedCommand(
|
gold = 0,
|
||||||
gold = 0,
|
food = 0,
|
||||||
food = 0,
|
originProvince = originProvince.id,
|
||||||
originProvince = originProvince.id,
|
destinationProvinceId = destinationProvince.id,
|
||||||
destinationProvinceId = destinationProvince.id,
|
marchingUnits = Vector(
|
||||||
marchingUnits = Vector(
|
CombatUnit(
|
||||||
CombatUnit(
|
factionId = actingFactionId,
|
||||||
factionId = actingFactionId,
|
heroId = leaderIdToMove,
|
||||||
heroId = leaderIdToMove,
|
battalionId = None
|
||||||
battalionId = None
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
),
|
)
|
||||||
reason = "Spreading out the faction leaders"
|
),
|
||||||
)
|
reason = "Spreading out the faction leaders"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.headOption
|
}.headOption
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
package net.eagle0.eagle.ai
|
package net.eagle0.eagle.ai
|
||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand, MarchCommandFromOneProvince}
|
||||||
AvailableCommand,
|
|
||||||
MarchAvailableCommand,
|
|
||||||
MarchCommandFromOneProvince
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.internal.faction.Faction
|
import net.eagle0.eagle.internal.faction.Faction
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.util.CommandSelection
|
import net.eagle0.eagle.library.util.CommandSelection
|
||||||
@@ -30,8 +26,7 @@ object MoveLeaderToBetterProvinceCommandChooser {
|
|||||||
actingFactionId = actingFactionId,
|
actingFactionId = actingFactionId,
|
||||||
gs = gs,
|
gs = gs,
|
||||||
acs = acs,
|
acs = acs,
|
||||||
betterDestinationChooser =
|
betterDestinationChooser = FactionLeaderProvinceRanker.bestSettlementPidForFactionLeader
|
||||||
FactionLeaderProvinceRanker.bestSettlementPidForFactionLeader
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private def candidatesForMarchCommand(
|
private def candidatesForMarchCommand(
|
||||||
@@ -57,22 +52,21 @@ object MoveLeaderToBetterProvinceCommandChooser {
|
|||||||
actingFactionId,
|
actingFactionId,
|
||||||
mcfop.originProvinceId,
|
mcfop.originProvinceId,
|
||||||
gs
|
gs
|
||||||
)
|
).flatMap { desiredPid =>
|
||||||
.flatMap { desiredPid =>
|
ProvinceDistances
|
||||||
ProvinceDistances
|
.closestProvinceThroughFriendliesOption(
|
||||||
.closestProvinceThroughFriendliesOption(
|
desiredPid,
|
||||||
desiredPid,
|
mcfop.availableDestinationProvinces.map(_.provinceId).toVector,
|
||||||
mcfop.availableDestinationProvinces.map(_.provinceId).toVector,
|
actingFactionId,
|
||||||
actingFactionId,
|
gs
|
||||||
gs
|
)
|
||||||
|
.map { provinceAndDistance =>
|
||||||
|
MCFOPWithDestinationAndDistance(
|
||||||
|
mcfop = mcfop,
|
||||||
|
provinceAndDistance = provinceAndDistance
|
||||||
)
|
)
|
||||||
.map { provinceAndDistance =>
|
}
|
||||||
MCFOPWithDestinationAndDistance(
|
}
|
||||||
mcfop = mcfop,
|
|
||||||
provinceAndDistance = provinceAndDistance
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def maybeMoveLeaderToBetterProvinceCommandWithChooser(
|
def maybeMoveLeaderToBetterProvinceCommandWithChooser(
|
||||||
@@ -82,15 +76,14 @@ object MoveLeaderToBetterProvinceCommandChooser {
|
|||||||
betterDestinationChooser: DestinationChooser
|
betterDestinationChooser: DestinationChooser
|
||||||
): Option[CommandSelection] = {
|
): Option[CommandSelection] = {
|
||||||
val cs = for {
|
val cs = for {
|
||||||
faction <- gs.factions.get(actingFactionId)
|
faction <- gs.factions.get(actingFactionId)
|
||||||
marchCommand <- acs.collectFirst { case ac: MarchAvailableCommand => ac }
|
marchCommand <- acs.collectFirst { case ac: MarchAvailableCommand => ac }
|
||||||
} yield {
|
} yield {
|
||||||
// Find the march command origin provinces that contain a faction leader
|
// Find the march command origin provinces that contain a faction leader
|
||||||
val candidatesWithDistances = candidateCommandsWithDistances(
|
val candidatesWithDistances = candidateCommandsWithDistances(
|
||||||
actingFactionId = actingFactionId,
|
actingFactionId = actingFactionId,
|
||||||
gs = gs,
|
gs = gs,
|
||||||
candidateCommands =
|
candidateCommands = candidatesForMarchCommand(marchCommand, gs, faction),
|
||||||
candidatesForMarchCommand(marchCommand, gs, faction),
|
|
||||||
betterDestinationChooser = betterDestinationChooser
|
betterDestinationChooser = betterDestinationChooser
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -77,23 +77,24 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
randomSelectionForType[ResolveInvitationAvailableCommand](
|
randomSelectionForType[ResolveInvitationAvailableCommand](
|
||||||
availableCommands,
|
availableCommands,
|
||||||
functionalRandom
|
functionalRandom
|
||||||
) { case (resolveInvitationAc, fr) =>
|
) {
|
||||||
selectionForResolveInvitationCommand(
|
case (resolveInvitationAc, fr) =>
|
||||||
actingFactionId = actingFactionId,
|
selectionForResolveInvitationCommand(
|
||||||
ac = resolveInvitationAc,
|
actingFactionId = actingFactionId,
|
||||||
gs = gameState,
|
ac = resolveInvitationAc,
|
||||||
functionalRandom = fr
|
gs = gameState,
|
||||||
).map { resolveInviteSelection =>
|
functionalRandom = fr
|
||||||
Some(
|
).map { resolveInviteSelection =>
|
||||||
CommandSelection(
|
Some(
|
||||||
actingFactionId = actingFactionId,
|
CommandSelection(
|
||||||
actingProvinceId = 0,
|
actingFactionId = actingFactionId,
|
||||||
available = resolveInvitationAc,
|
actingProvinceId = 0,
|
||||||
selected = resolveInviteSelection,
|
available = resolveInvitationAc,
|
||||||
reason = "resolving invitation"
|
selected = resolveInviteSelection,
|
||||||
|
reason = "resolving invitation"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def selectionForResolveInvitationCommand(
|
private def selectionForResolveInvitationCommand(
|
||||||
@@ -102,11 +103,9 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
gs: GameState,
|
gs: GameState,
|
||||||
functionalRandom: FunctionalRandom
|
functionalRandom: FunctionalRandom
|
||||||
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
||||||
val invitedFid = actingFactionId
|
val invitedFid = actingFactionId
|
||||||
val bestInvitation = ac.invitations
|
val bestInvitation = ac.invitations
|
||||||
.maxBy(inv =>
|
.maxBy(inv => invitationAcceptanceChance(inv.originatingFactionId, invitedFid, gs))
|
||||||
invitationAcceptanceChance(inv.originatingFactionId, invitedFid, gs)
|
|
||||||
)
|
|
||||||
|
|
||||||
internalRequire(
|
internalRequire(
|
||||||
bestInvitation.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
bestInvitation.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
||||||
@@ -150,23 +149,24 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
randomSelectionForType[ResolveTruceOfferAvailableCommand](
|
randomSelectionForType[ResolveTruceOfferAvailableCommand](
|
||||||
availableCommands,
|
availableCommands,
|
||||||
functionalRandom
|
functionalRandom
|
||||||
) { case (resolveTruceAc, fr) =>
|
) {
|
||||||
selectionForResolveTruceOfferSelectedCommand(
|
case (resolveTruceAc, fr) =>
|
||||||
actingFactionId = actingFactionId,
|
selectionForResolveTruceOfferSelectedCommand(
|
||||||
ac = resolveTruceAc,
|
actingFactionId = actingFactionId,
|
||||||
gs = gameState,
|
ac = resolveTruceAc,
|
||||||
functionalRandom = fr
|
gs = gameState,
|
||||||
).map { resolveTruceOfferSelection =>
|
functionalRandom = fr
|
||||||
Some(
|
).map { resolveTruceOfferSelection =>
|
||||||
CommandSelection(
|
Some(
|
||||||
actingFactionId = actingFactionId,
|
CommandSelection(
|
||||||
actingProvinceId = 0,
|
actingFactionId = actingFactionId,
|
||||||
available = resolveTruceAc,
|
actingProvinceId = 0,
|
||||||
selected = resolveTruceOfferSelection,
|
available = resolveTruceAc,
|
||||||
reason = "resolving truce offer"
|
selected = resolveTruceOfferSelection,
|
||||||
|
reason = "resolving truce offer"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def selectionForResolveTruceOfferSelectedCommand(
|
private def selectionForResolveTruceOfferSelectedCommand(
|
||||||
@@ -177,9 +177,7 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
||||||
val actingFid = actingFactionId
|
val actingFid = actingFactionId
|
||||||
val bestOffer = ac.offers
|
val bestOffer = ac.offers
|
||||||
.maxBy(inv =>
|
.maxBy(inv => truceOfferAcceptanceChance(inv.originatingFactionId, actingFid, gs))
|
||||||
truceOfferAcceptanceChance(inv.originatingFactionId, actingFid, gs)
|
|
||||||
)
|
|
||||||
|
|
||||||
internalRequire(
|
internalRequire(
|
||||||
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
||||||
@@ -225,7 +223,7 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
originatingFid: FactionId,
|
originatingFid: FactionId,
|
||||||
targetFid: FactionId,
|
targetFid: FactionId,
|
||||||
gs: GameState
|
gs: GameState
|
||||||
): Int = {
|
): Int =
|
||||||
// Always reject if the faction already has an alliance
|
// Always reject if the faction already has an alliance
|
||||||
if gs
|
if gs
|
||||||
.factions(targetFid)
|
.factions(targetFid)
|
||||||
@@ -235,11 +233,10 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
|
|
||||||
// reject if this is our only neighbor and we have no expansion possibilities
|
// reject if this is our only neighbor and we have no expansion possibilities
|
||||||
else if LegacyFactionUtils.provinces(targetFid, gs).forall { province =>
|
else if LegacyFactionUtils.provinces(targetFid, gs).forall { province =>
|
||||||
province.neighbors.map { n => gs.provinces(n.provinceId) }.forall {
|
province.neighbors.map(n => gs.provinces(n.provinceId)).forall { neighborProvince =>
|
||||||
neighborProvince =>
|
neighborProvince.rulingFactionId.contains(
|
||||||
neighborProvince.rulingFactionId.contains(
|
originatingFid
|
||||||
originatingFid
|
) || neighborProvince.rulingFactionId.contains(targetFid)
|
||||||
) || neighborProvince.rulingFactionId.contains(targetFid)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
then 0
|
then 0
|
||||||
@@ -254,26 +251,24 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
gameState = gs
|
gameState = gs
|
||||||
)
|
)
|
||||||
).floor.toInt.max(0)
|
).floor.toInt.max(0)
|
||||||
}
|
|
||||||
|
|
||||||
private def resolveRansomOfferSelectedCommand(
|
private def resolveRansomOfferSelectedCommand(
|
||||||
actingFactionId: FactionId,
|
actingFactionId: FactionId,
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
availableCommands: Vector[AvailableCommand]
|
availableCommands: Vector[AvailableCommand]
|
||||||
): Option[CommandSelection] =
|
): Option[CommandSelection] =
|
||||||
selectionForType[ResolveRansomOfferAvailableCommand](availableCommands) {
|
selectionForType[ResolveRansomOfferAvailableCommand](availableCommands) { resolveRansomAc =>
|
||||||
resolveRansomAc =>
|
CommandSelection(
|
||||||
CommandSelection(
|
actingFactionId = actingFactionId,
|
||||||
|
actingProvinceId = 0,
|
||||||
|
available = resolveRansomAc,
|
||||||
|
selected = selectionForResolveRansomOfferSelectedCommand(
|
||||||
actingFactionId = actingFactionId,
|
actingFactionId = actingFactionId,
|
||||||
actingProvinceId = 0,
|
ac = resolveRansomAc,
|
||||||
available = resolveRansomAc,
|
gs = gameState
|
||||||
selected = selectionForResolveRansomOfferSelectedCommand(
|
),
|
||||||
actingFactionId = actingFactionId,
|
reason = "resolving ransom offer"
|
||||||
ac = resolveRansomAc,
|
)
|
||||||
gs = gameState
|
|
||||||
),
|
|
||||||
reason = "resolving ransom offer"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def selectionForResolveRansomOfferSelectedCommand(
|
private def selectionForResolveRansomOfferSelectedCommand(
|
||||||
@@ -288,7 +283,7 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
ransomOffer = Some(ac.offers.head),
|
ransomOffer = Some(ac.offers.head),
|
||||||
resolution = RansomOfferHelpers.chosenResolution(rod)
|
resolution = RansomOfferHelpers.chosenResolution(rod)
|
||||||
)
|
)
|
||||||
case _ => throw new EagleInternalException("Not a ransom offer")
|
case _ => throw new EagleInternalException("Not a ransom offer")
|
||||||
}
|
}
|
||||||
|
|
||||||
private def resolveAllianceOfferSelectedCommand(
|
private def resolveAllianceOfferSelectedCommand(
|
||||||
@@ -300,23 +295,24 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
randomSelectionForType[ResolveAllianceOfferAvailableCommand](
|
randomSelectionForType[ResolveAllianceOfferAvailableCommand](
|
||||||
availableCommands,
|
availableCommands,
|
||||||
functionalRandom
|
functionalRandom
|
||||||
) { case (resolveAllianceAc, fr) =>
|
) {
|
||||||
selectionForResolveAllianceOfferSelectedCommand(
|
case (resolveAllianceAc, fr) =>
|
||||||
actingFactionId = actingFactionId,
|
selectionForResolveAllianceOfferSelectedCommand(
|
||||||
ac = resolveAllianceAc,
|
actingFactionId = actingFactionId,
|
||||||
gs = gameState,
|
ac = resolveAllianceAc,
|
||||||
functionalRandom = fr
|
gs = gameState,
|
||||||
).map { resolveAllianceOfferSelection =>
|
functionalRandom = fr
|
||||||
Some(
|
).map { resolveAllianceOfferSelection =>
|
||||||
CommandSelection(
|
Some(
|
||||||
actingFactionId = actingFactionId,
|
CommandSelection(
|
||||||
actingProvinceId = 0,
|
actingFactionId = actingFactionId,
|
||||||
available = resolveAllianceAc,
|
actingProvinceId = 0,
|
||||||
selected = resolveAllianceOfferSelection,
|
available = resolveAllianceAc,
|
||||||
reason = "resolving alliance offer"
|
selected = resolveAllianceOfferSelection,
|
||||||
|
reason = "resolving alliance offer"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def resolveBreakAllianceSelectedCommand(
|
private def resolveBreakAllianceSelectedCommand(
|
||||||
@@ -328,23 +324,24 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
randomSelectionForType[ResolveBreakAllianceAvailableCommand](
|
randomSelectionForType[ResolveBreakAllianceAvailableCommand](
|
||||||
availableCommands,
|
availableCommands,
|
||||||
functionalRandom
|
functionalRandom
|
||||||
) { case (resolveBreakAllianceAc, fr) =>
|
) {
|
||||||
selectionForResolveBreakAllianceSelectedCommand(
|
case (resolveBreakAllianceAc, fr) =>
|
||||||
actingFactionId = actingFactionId,
|
selectionForResolveBreakAllianceSelectedCommand(
|
||||||
ac = resolveBreakAllianceAc,
|
actingFactionId = actingFactionId,
|
||||||
gs = gameState,
|
ac = resolveBreakAllianceAc,
|
||||||
functionalRandom = fr
|
gs = gameState,
|
||||||
).map { resolveBreakAllianceSelection =>
|
functionalRandom = fr
|
||||||
Some(
|
).map { resolveBreakAllianceSelection =>
|
||||||
CommandSelection(
|
Some(
|
||||||
actingFactionId = actingFactionId,
|
CommandSelection(
|
||||||
actingProvinceId = 0,
|
actingFactionId = actingFactionId,
|
||||||
available = resolveBreakAllianceAc,
|
actingProvinceId = 0,
|
||||||
selected = resolveBreakAllianceSelection,
|
available = resolveBreakAllianceAc,
|
||||||
reason = "resolving break alliance"
|
selected = resolveBreakAllianceSelection,
|
||||||
|
reason = "resolving break alliance"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def selectionForResolveAllianceOfferSelectedCommand(
|
private def selectionForResolveAllianceOfferSelectedCommand(
|
||||||
@@ -355,9 +352,7 @@ object ResolveDiplomacyCommandSelector {
|
|||||||
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
||||||
val actingFid = actingFactionId
|
val actingFid = actingFactionId
|
||||||
val bestOffer = ac.offers
|
val bestOffer = ac.offers
|
||||||
.maxBy(inv =>
|
.maxBy(inv => allianceOfferAcceptanceChance(inv.originatingFactionId, actingFid, gs))
|
||||||
allianceOfferAcceptanceChance(inv.originatingFactionId, actingFid, gs)
|
|
||||||
)
|
|
||||||
|
|
||||||
internalRequire(
|
internalRequire(
|
||||||
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
package net.eagle0.eagle.ai
|
package net.eagle0.eagle.ai
|
||||||
|
|
||||||
import net.eagle0.common.MoreOption
|
import net.eagle0.common.MoreOption
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand}
|
||||||
AvailableCommand,
|
|
||||||
MarchAvailableCommand
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
||||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.hero.Hero
|
import net.eagle0.eagle.internal.hero.Hero
|
||||||
import net.eagle0.eagle.internal.province.Province
|
import net.eagle0.eagle.internal.province.Province
|
||||||
import net.eagle0.eagle.library.settings.{
|
import net.eagle0.eagle.library.settings.{LoyaltyGainFromFeast, MinimumLoyaltyForSwearBrotherhood}
|
||||||
LoyaltyGainFromFeast,
|
|
||||||
MinimumLoyaltyForSwearBrotherhood
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.{CommandSelection, ProvinceDistances}
|
import net.eagle0.eagle.library.util.{CommandSelection, ProvinceDistances}
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
||||||
CommandChoiceHelpers,
|
CommandChoiceHelpers,
|
||||||
@@ -43,21 +37,22 @@ object SeekMoreLeadersCommandChooser {
|
|||||||
factionHeadProvince: Province,
|
factionHeadProvince: Province,
|
||||||
gameState: GameState
|
gameState: GameState
|
||||||
): Option[CommandSelection] = {
|
): Option[CommandSelection] = {
|
||||||
val opmc = command.oneProvinceCommands
|
val opmc = command.oneProvinceCommands
|
||||||
.find(_.originProvinceId == startingProvince.id)
|
.find(_.originProvinceId == startingProvince.id)
|
||||||
.get
|
.get
|
||||||
val bestDestination =
|
val bestDestination =
|
||||||
opmc.availableDestinationProvinces
|
opmc.availableDestinationProvinces
|
||||||
.map(dest => (opmc, gameState.provinces(dest.provinceId)))
|
.map(dest => (opmc, gameState.provinces(dest.provinceId)))
|
||||||
.flatMap { case (opmc, p) =>
|
.flatMap {
|
||||||
ProvinceDistances
|
case (opmc, p) =>
|
||||||
.distanceThroughFriendliesOption(
|
ProvinceDistances
|
||||||
factionHeadProvince.id,
|
.distanceThroughFriendliesOption(
|
||||||
p.id,
|
factionHeadProvince.id,
|
||||||
factionHeadProvince.rulingFactionId.get,
|
p.id,
|
||||||
gameState
|
factionHeadProvince.rulingFactionId.get,
|
||||||
)
|
gameState
|
||||||
.map(distance => (opmc, p, distance))
|
)
|
||||||
|
.map(distance => (opmc, p, distance))
|
||||||
}
|
}
|
||||||
.minByOption(_._3)
|
.minByOption(_._3)
|
||||||
|
|
||||||
@@ -88,7 +83,7 @@ object SeekMoreLeadersCommandChooser {
|
|||||||
actingFactionId: FactionId,
|
actingFactionId: FactionId,
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
acs: Vector[AvailableCommand]
|
acs: Vector[AvailableCommand]
|
||||||
): Option[CommandSelection] = {
|
): Option[CommandSelection] =
|
||||||
LegacyFactionUtils
|
LegacyFactionUtils
|
||||||
.provinces(actingFactionId, gameState)
|
.provinces(actingFactionId, gameState)
|
||||||
.find(
|
.find(
|
||||||
@@ -106,36 +101,34 @@ object SeekMoreLeadersCommandChooser {
|
|||||||
province.rulingFactionHeroIds.map(gameState.heroes),
|
province.rulingFactionHeroIds.map(gameState.heroes),
|
||||||
gameState.factions(actingFactionId).leaders.toVector
|
gameState.factions(actingFactionId).leaders.toVector
|
||||||
)
|
)
|
||||||
.map { hero => Candidate(hero, province) }
|
.map(hero => Candidate(hero, province))
|
||||||
}
|
}
|
||||||
|
|
||||||
val marchACs = acs
|
val marchACs = acs.collect { case ac: MarchAvailableCommand => ac }
|
||||||
.collect { case ac: MarchAvailableCommand => ac }
|
|
||||||
|
|
||||||
val marchableCandidates = candidates.flatMap { c =>
|
val marchableCandidates = candidates.flatMap { c =>
|
||||||
marchACs
|
marchACs.find { mac =>
|
||||||
.find { mac =>
|
mac.oneProvinceCommands.exists(opmc =>
|
||||||
mac.oneProvinceCommands.exists(opmc =>
|
opmc.originProvinceId == c.province.id && opmc.availableHeroIds.toVector
|
||||||
opmc.originProvinceId == c.province.id && opmc.availableHeroIds.toVector
|
.contains(c.hero.id)
|
||||||
.contains(c.hero.id)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
.map(mac => (mac, c))
|
.map(mac => (mac, c))
|
||||||
}
|
}
|
||||||
|
|
||||||
marchableCandidates
|
marchableCandidates
|
||||||
.maxByOption(x => LegacyHeroUtils.power(x._2.hero))
|
.maxByOption(x => LegacyHeroUtils.power(x._2.hero))
|
||||||
.flatMap { case (command, candidate) =>
|
.flatMap {
|
||||||
maybeMoveTowardDestination(
|
case (command, candidate) =>
|
||||||
command = command,
|
maybeMoveTowardDestination(
|
||||||
hero = candidate.hero,
|
command = command,
|
||||||
startingProvince = candidate.province,
|
hero = candidate.hero,
|
||||||
factionHeadProvince = provinceWithFactionHead,
|
startingProvince = candidate.province,
|
||||||
gameState = gameState
|
factionHeadProvince = provinceWithFactionHead,
|
||||||
)
|
gameState = gameState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def maybeSeekMoreLeadersCommand(
|
def maybeSeekMoreLeadersCommand(
|
||||||
actingFactionId: FactionId,
|
actingFactionId: FactionId,
|
||||||
@@ -154,37 +147,36 @@ object SeekMoreLeadersCommandChooser {
|
|||||||
faction.leaders.toVector
|
faction.leaders.toVector
|
||||||
)
|
)
|
||||||
|
|
||||||
desiredHero
|
desiredHero.flatMap { hero =>
|
||||||
.flatMap { hero =>
|
SwearBrotherhoodCommandSelector
|
||||||
SwearBrotherhoodCommandSelector
|
.chosenCommandForSpecificHero(
|
||||||
.chosenCommandForSpecificHero(
|
actingFactionId = actingFactionId,
|
||||||
actingFactionId = actingFactionId,
|
gameState = gameState,
|
||||||
gameState = gameState,
|
availableCommands = acs,
|
||||||
availableCommands = acs,
|
desiredHeroId = hero.id
|
||||||
desiredHeroId = hero.id
|
)
|
||||||
)
|
.orElse {
|
||||||
.orElse {
|
MoreOption.flatWhen(
|
||||||
MoreOption.flatWhen(
|
MinimumLoyaltyForSwearBrotherhood.doubleValue - hero.loyalty < LoyaltyGainFromFeast.doubleValue
|
||||||
MinimumLoyaltyForSwearBrotherhood.doubleValue - hero.loyalty < LoyaltyGainFromFeast.doubleValue
|
) {
|
||||||
) {
|
CommandChoiceHelpers.chosenFeastCommandIfAvailable(
|
||||||
CommandChoiceHelpers.chosenFeastCommandIfAvailable(
|
|
||||||
actingFactionId = actingFactionId,
|
|
||||||
gameState = gameState,
|
|
||||||
availableCommands = acs,
|
|
||||||
reason = s"want to make hero ${hero.id} a leader"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.orElse {
|
|
||||||
HeroGiftCommandSelector.chosenCommandForSpecificHero(
|
|
||||||
actingFactionId = actingFactionId,
|
actingFactionId = actingFactionId,
|
||||||
gameState = gameState,
|
gameState = gameState,
|
||||||
availableCommands = acs,
|
availableCommands = acs,
|
||||||
heroId = hero.id,
|
reason = s"want to make hero ${hero.id} a leader"
|
||||||
reason = "want to make this hero a leader"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.orElse {
|
||||||
|
HeroGiftCommandSelector.chosenCommandForSpecificHero(
|
||||||
|
actingFactionId = actingFactionId,
|
||||||
|
gameState = gameState,
|
||||||
|
availableCommands = acs,
|
||||||
|
heroId = hero.id,
|
||||||
|
reason = "want to make this hero a leader"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
.orElse(
|
.orElse(
|
||||||
// Then try moving a hero this way
|
// Then try moving a hero this way
|
||||||
maybeMoveBestCandidateCommand(
|
maybeMoveBestCandidateCommand(
|
||||||
|
|||||||
@@ -37,5 +37,5 @@ case class UnrequestedClientText(
|
|||||||
llmRequest: GeneratedTextRequest
|
llmRequest: GeneratedTextRequest
|
||||||
) extends ClientText {
|
) extends ClientText {
|
||||||
def complete: Boolean = false
|
def complete: Boolean = false
|
||||||
def text: String = ""
|
def text: String = ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,7 @@ import scala.util.Try
|
|||||||
|
|
||||||
import net.eagle0.common.ProtoParser
|
import net.eagle0.common.ProtoParser
|
||||||
import net.eagle0.eagle.{ClientTextId, FactionId}
|
import net.eagle0.eagle.{ClientTextId, FactionId}
|
||||||
import net.eagle0.eagle.internal.client_text.{
|
import net.eagle0.eagle.internal.client_text.{IncompleteText, IncompleteTextStore, UnrequestedText}
|
||||||
IncompleteText,
|
|
||||||
IncompleteTextStore,
|
|
||||||
UnrequestedText
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.internal.generated_text_request.GeneratedTextRequest
|
import net.eagle0.eagle.internal.generated_text_request.GeneratedTextRequest
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||||
import net.eagle0.eagle.library.EagleInternalException
|
import net.eagle0.eagle.library.EagleInternalException
|
||||||
@@ -56,7 +52,7 @@ case class ClientTextStoreImpl(
|
|||||||
requestedAfterHistoryCount = requestedAfterHistoryCount,
|
requestedAfterHistoryCount = requestedAfterHistoryCount,
|
||||||
llmRequest = llmRequest
|
llmRequest = llmRequest
|
||||||
)),
|
)),
|
||||||
accessibleTo = this.accessibleTo + (id -> accessibleTo),
|
accessibleTo = this.accessibleTo + (id -> accessibleTo),
|
||||||
accessibleToIsSaved = false,
|
accessibleToIsSaved = false,
|
||||||
incompleteTextsAreSaved = false
|
incompleteTextsAreSaved = false
|
||||||
)
|
)
|
||||||
@@ -73,8 +69,7 @@ case class ClientTextStoreImpl(
|
|||||||
id = id,
|
id = id,
|
||||||
partialText = "",
|
partialText = "",
|
||||||
llmRequest = unrequested.llmRequest,
|
llmRequest = unrequested.llmRequest,
|
||||||
requestedAfterHistoryCount =
|
requestedAfterHistoryCount = unrequested.requestedAfterHistoryCount
|
||||||
unrequested.requestedAfterHistoryCount
|
|
||||||
)),
|
)),
|
||||||
unrequestedTexts = unrequestedTexts - id,
|
unrequestedTexts = unrequestedTexts - id,
|
||||||
incompleteTextsAreSaved = false
|
incompleteTextsAreSaved = false
|
||||||
@@ -98,7 +93,7 @@ case class ClientTextStoreImpl(
|
|||||||
.orElse {
|
.orElse {
|
||||||
completeTexts.get(id).map(_.text)
|
completeTexts.get(id).map(_.text)
|
||||||
}
|
}
|
||||||
.map { text => TextGenerationSuccess(text) }
|
.map(text => TextGenerationSuccess(text))
|
||||||
.getOrElse {
|
.getOrElse {
|
||||||
incompleteTexts
|
incompleteTexts
|
||||||
.get(id)
|
.get(id)
|
||||||
@@ -172,7 +167,7 @@ case class ClientTextStoreImpl(
|
|||||||
addedFactionIds: Vector[FactionId]
|
addedFactionIds: Vector[FactionId]
|
||||||
): ClientTextStore = {
|
): ClientTextStore = {
|
||||||
val originalFactionIds = accessibleTo.getOrElse(id, Vector()).sorted
|
val originalFactionIds = accessibleTo.getOrElse(id, Vector()).sorted
|
||||||
val updatedFactionIds =
|
val updatedFactionIds =
|
||||||
(originalFactionIds ++ addedFactionIds).distinct.sorted
|
(originalFactionIds ++ addedFactionIds).distinct.sorted
|
||||||
|
|
||||||
if updatedFactionIds == originalFactionIds then this
|
if updatedFactionIds == originalFactionIds then this
|
||||||
@@ -206,22 +201,22 @@ case class ClientTextStoreImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
object ClientTextStoreImpl {
|
object ClientTextStoreImpl {
|
||||||
private val separator = "\n******\n"
|
private val separator = "\n******\n"
|
||||||
private val separatorPattern = Pattern.quote(separator)
|
private val separatorPattern = Pattern.quote(separator)
|
||||||
|
|
||||||
private def completeTextsKey = "completeText.txt"
|
private def completeTextsKey = "completeText.txt"
|
||||||
private def incompleteTextsKey = "incompleteText.dat"
|
private def incompleteTextsKey = "incompleteText.dat"
|
||||||
private def visibilityKey = "visibility.txt"
|
private def visibilityKey = "visibility.txt"
|
||||||
|
|
||||||
def saved(clientTextStore: ClientTextStoreImpl): ClientTextStore = {
|
def saved(clientTextStore: ClientTextStoreImpl): ClientTextStore = {
|
||||||
val completeSaved =
|
val completeSaved =
|
||||||
if clientTextStore.completeTexts.size > clientTextStore.savedCompleteCount
|
if clientTextStore.completeTexts.size > clientTextStore.savedCompleteCount
|
||||||
then {
|
then {
|
||||||
// FIXME: make this append, will require Persister changes
|
// FIXME: make this append, will require Persister changes
|
||||||
val saveData = clientTextStore.completeTexts
|
val saveData = clientTextStore.completeTexts.map {
|
||||||
.map { case (_: String, ct: CompleteClientText) =>
|
case (_: String, ct: CompleteClientText) =>
|
||||||
s"${ct.id}\n${ct.requestedAfterHistoryCount}\n${ct.text}$separator"
|
s"${ct.id}\n${ct.requestedAfterHistoryCount}\n${ct.text}$separator"
|
||||||
}
|
}
|
||||||
.mkString("")
|
.mkString("")
|
||||||
.getBytes(StandardCharsets.UTF_8)
|
.getBytes(StandardCharsets.UTF_8)
|
||||||
|
|
||||||
@@ -231,9 +226,7 @@ object ClientTextStoreImpl {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientTextStore.copy(savedCompleteCount =
|
clientTextStore.copy(savedCompleteCount = clientTextStore.completeTexts.size)
|
||||||
clientTextStore.completeTexts.size
|
|
||||||
)
|
|
||||||
} else clientTextStore
|
} else clientTextStore
|
||||||
|
|
||||||
val incompleteSaved =
|
val incompleteSaved =
|
||||||
@@ -270,10 +263,10 @@ object ClientTextStoreImpl {
|
|||||||
|
|
||||||
val accessibleToSaved =
|
val accessibleToSaved =
|
||||||
if !incompleteSaved.accessibleToIsSaved then {
|
if !incompleteSaved.accessibleToIsSaved then {
|
||||||
val accessibleToData = incompleteSaved.accessibleTo
|
val accessibleToData = incompleteSaved.accessibleTo.map {
|
||||||
.map { case (id, factions) =>
|
case (id, factions) =>
|
||||||
s"$id\n${factions.mkString(",")}$separator"
|
s"$id\n${factions.mkString(",")}$separator"
|
||||||
}
|
}
|
||||||
.mkString("")
|
.mkString("")
|
||||||
.getBytes(StandardCharsets.UTF_8)
|
.getBytes(StandardCharsets.UTF_8)
|
||||||
|
|
||||||
@@ -316,9 +309,10 @@ object ClientTextStoreImpl {
|
|||||||
}
|
}
|
||||||
.toVector
|
.toVector
|
||||||
}
|
}
|
||||||
.recover { case _: FileNotFoundException =>
|
.recover {
|
||||||
println("No complete text store found")
|
case _: FileNotFoundException =>
|
||||||
Vector()
|
println("No complete text store found")
|
||||||
|
Vector()
|
||||||
}
|
}
|
||||||
|
|
||||||
private def loadedIncompleteTexts(
|
private def loadedIncompleteTexts(
|
||||||
@@ -350,9 +344,10 @@ object ClientTextStoreImpl {
|
|||||||
}
|
}
|
||||||
.getOrElse((Vector(), Vector()))
|
.getOrElse((Vector(), Vector()))
|
||||||
}
|
}
|
||||||
.recover { case _: FileNotFoundException =>
|
.recover {
|
||||||
println("No incomplete text store found")
|
case _: FileNotFoundException =>
|
||||||
(Vector(), Vector())
|
println("No incomplete text store found")
|
||||||
|
(Vector(), Vector())
|
||||||
}
|
}
|
||||||
|
|
||||||
private def loadedAccessibleTo(
|
private def loadedAccessibleTo(
|
||||||
@@ -376,9 +371,10 @@ object ClientTextStoreImpl {
|
|||||||
}
|
}
|
||||||
.toMap
|
.toMap
|
||||||
}
|
}
|
||||||
.recover { case _: FileNotFoundException =>
|
.recover {
|
||||||
println("No accessibleTo store found")
|
case _: FileNotFoundException =>
|
||||||
Map()
|
println("No accessibleTo store found")
|
||||||
|
Map()
|
||||||
}
|
}
|
||||||
|
|
||||||
def loaded(
|
def loaded(
|
||||||
@@ -386,22 +382,18 @@ object ClientTextStoreImpl {
|
|||||||
persister: Persister
|
persister: Persister
|
||||||
): Try[ClientTextStore] =
|
): Try[ClientTextStore] =
|
||||||
for {
|
for {
|
||||||
loadedCompletedTexts <- loadedCompleteTexts(persister)
|
loadedCompletedTexts <- loadedCompleteTexts(persister)
|
||||||
loadedIncompleteTexts <- loadedIncompleteTexts(persister)
|
loadedIncompleteTexts <- loadedIncompleteTexts(persister)
|
||||||
loadedAccessibleTo <- loadedAccessibleTo(persister)
|
loadedAccessibleTo <- loadedAccessibleTo(persister)
|
||||||
} yield {
|
} yield ClientTextStoreImpl(
|
||||||
ClientTextStoreImpl(
|
pregenerated = pregenerated,
|
||||||
pregenerated = pregenerated,
|
persister = persister,
|
||||||
persister = persister,
|
completeTexts = loadedCompletedTexts.map(ct => ct.id -> ct).toMap,
|
||||||
completeTexts = loadedCompletedTexts.map(ct => ct.id -> ct).toMap,
|
incompleteTexts = loadedIncompleteTexts._1.map(ict => ict.id -> ict).toMap,
|
||||||
incompleteTexts =
|
unrequestedTexts = loadedIncompleteTexts._2.map(ut => ut.id -> ut).toMap,
|
||||||
loadedIncompleteTexts._1.map(ict => ict.id -> ict).toMap,
|
accessibleTo = loadedAccessibleTo,
|
||||||
unrequestedTexts =
|
savedCompleteCount = loadedCompletedTexts.size,
|
||||||
loadedIncompleteTexts._2.map(ut => ut.id -> ut).toMap,
|
incompleteTextsAreSaved = true,
|
||||||
accessibleTo = loadedAccessibleTo,
|
accessibleToIsSaved = true
|
||||||
savedCompleteCount = loadedCompletedTexts.size,
|
)
|
||||||
incompleteTextsAreSaved = true,
|
|
||||||
accessibleToIsSaved = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ object TextGenerationResult {
|
|||||||
acc :+ result,
|
acc :+ result,
|
||||||
tail
|
tail
|
||||||
)
|
)
|
||||||
case x => x
|
case x => x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,30 +47,28 @@ trait TextGenerationResult {
|
|||||||
}
|
}
|
||||||
case class TextGenerationSuccess(result: String) extends TextGenerationResult {
|
case class TextGenerationSuccess(result: String) extends TextGenerationResult {
|
||||||
override def resolved: Boolean = true
|
override def resolved: Boolean = true
|
||||||
override def get: String = result
|
override def get: String = result
|
||||||
}
|
}
|
||||||
case class TextGenerationDependencyInProgress(
|
case class TextGenerationDependencyInProgress(
|
||||||
inProgressTextId: String,
|
inProgressTextId: String,
|
||||||
partialText: String
|
partialText: String
|
||||||
) extends TextGenerationResult {
|
) extends TextGenerationResult {
|
||||||
override def resolved: Boolean = false
|
override def resolved: Boolean = false
|
||||||
override def get: String =
|
override def get: String =
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
s"Text generation dependency in progress: $inProgressTextId"
|
s"Text generation dependency in progress: $inProgressTextId"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case class TextGenerationDependencyWaiting(notSatisfiedTextId: String)
|
case class TextGenerationDependencyWaiting(notSatisfiedTextId: String) extends TextGenerationResult {
|
||||||
extends TextGenerationResult {
|
|
||||||
override def resolved: Boolean = false
|
override def resolved: Boolean = false
|
||||||
override def get: String =
|
override def get: String =
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
s"Text generation dependency not satisfied: $notSatisfiedTextId"
|
s"Text generation dependency not satisfied: $notSatisfiedTextId"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case class TextGenerationDependencyUnknown(unknownTextId: String)
|
case class TextGenerationDependencyUnknown(unknownTextId: String) extends TextGenerationResult {
|
||||||
extends TextGenerationResult {
|
|
||||||
override def resolved: Boolean = false
|
override def resolved: Boolean = false
|
||||||
override def get: String =
|
override def get: String =
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
s"Text generation dependency unknown: $unknownTextId"
|
s"Text generation dependency unknown: $unknownTextId"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ object ActionResultFilter {
|
|||||||
RoundPhase.HERO_DEPARTURES,
|
RoundPhase.HERO_DEPARTURES,
|
||||||
RoundPhase.UNCONTESTED_CONQUEST,
|
RoundPhase.UNCONTESTED_CONQUEST,
|
||||||
RoundPhase.BATTLE_RESOLUTION,
|
RoundPhase.BATTLE_RESOLUTION,
|
||||||
RoundPhase.BATTLE_AFTERMATH, // MIGHT BE WRONG
|
RoundPhase.BATTLE_AFTERMATH, // MIGHT BE WRONG
|
||||||
RoundPhase.ATTACK_DECISION, // MIGHT BE WRONG
|
RoundPhase.ATTACK_DECISION, // MIGHT BE WRONG
|
||||||
RoundPhase.DIPLOMACY_RESOLUTION // IS WRONG
|
RoundPhase.DIPLOMACY_RESOLUTION // IS WRONG
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,8 +97,7 @@ object ActionResultFilter {
|
|||||||
GameStateViewDiffer.diff(
|
GameStateViewDiffer.diff(
|
||||||
before = GameStateViewFilter
|
before = GameStateViewFilter
|
||||||
.filteredGameState(gs = before, factionId = factionId),
|
.filteredGameState(gs = before, factionId = factionId),
|
||||||
after =
|
after = GameStateViewFilter.filteredGameState(gs = after, factionId = factionId)
|
||||||
GameStateViewFilter.filteredGameState(gs = after, factionId = factionId)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private def transformOne(
|
private def transformOne(
|
||||||
@@ -115,9 +114,7 @@ object ActionResultFilter {
|
|||||||
)
|
)
|
||||||
|
|
||||||
def shouldInclude(note: Notification): Boolean =
|
def shouldInclude(note: Notification): Boolean =
|
||||||
note.targetFactions.isEmpty || factionId.exists(fid =>
|
note.targetFactions.isEmpty || factionId.exists(fid => note.targetFactions.exists(_.factionId == fid))
|
||||||
note.targetFactions.exists(_.factionId == fid)
|
|
||||||
)
|
|
||||||
|
|
||||||
if gsDiff.isDefined &&
|
if gsDiff.isDefined &&
|
||||||
result.actionResult.player.isDefined &&
|
result.actionResult.player.isDefined &&
|
||||||
@@ -143,8 +140,7 @@ object ActionResultFilter {
|
|||||||
province = actionResult.province,
|
province = actionResult.province,
|
||||||
leader = actionResult.leader,
|
leader = actionResult.leader,
|
||||||
gameStateDiff = gsDiff,
|
gameStateDiff = gsDiff,
|
||||||
notifications =
|
notifications = actionResult.notificationsToDeliver.filter(shouldInclude)
|
||||||
actionResult.notificationsToDeliver.filter(shouldInclude)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,10 +150,9 @@ object ActionResultFilter {
|
|||||||
results: Vector[ActionWithResultingState],
|
results: Vector[ActionWithResultingState],
|
||||||
factionId: Option[FactionId]
|
factionId: Option[FactionId]
|
||||||
): Vector[ActionResultView] =
|
): Vector[ActionResultView] =
|
||||||
factionId
|
factionId.map { pid =>
|
||||||
.map { pid =>
|
filterForPlayer(startingState, results, pid)
|
||||||
filterForPlayer(startingState, results, pid)
|
}
|
||||||
}
|
|
||||||
.getOrElse(filterForObserver(startingState, results))
|
.getOrElse(filterForObserver(startingState, results))
|
||||||
|
|
||||||
def filterForPlayer(
|
def filterForPlayer(
|
||||||
@@ -182,7 +177,5 @@ object ActionResultFilter {
|
|||||||
startingState: GameState,
|
startingState: GameState,
|
||||||
results: Vector[ActionWithResultingState]
|
results: Vector[ActionWithResultingState]
|
||||||
): Vector[ActionResultView] =
|
): Vector[ActionResultView] =
|
||||||
results.flatMap(res =>
|
results.flatMap(res => observeOne(result = res, startingState = startingState))
|
||||||
observeOne(result = res, startingState = startingState)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
package net.eagle0.eagle.library
|
package net.eagle0.eagle.library
|
||||||
|
|
||||||
class EagleValidationException(message: String)
|
class EagleValidationException(message: String) extends EagleInternalException(message) {}
|
||||||
extends EagleInternalException(message) {}
|
|
||||||
|
|||||||
@@ -8,24 +8,15 @@ import net.eagle0.eagle.api.command.AvailableCommands
|
|||||||
import net.eagle0.eagle.api.selected_command.SelectedCommand
|
import net.eagle0.eagle.api.selected_command.SelectedCommand
|
||||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.actions.applier.{
|
import net.eagle0.eagle.library.actions.applier.{ActionResultProtoApplier, ActionResultProtoApplierImpl}
|
||||||
ActionResultProtoApplier,
|
|
||||||
ActionResultProtoApplierImpl
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.actions.availability.AvailableCommandsFactory
|
import net.eagle0.eagle.library.actions.availability.AvailableCommandsFactory
|
||||||
import net.eagle0.eagle.library.actions.impl.action.{
|
import net.eagle0.eagle.library.actions.impl.action.{
|
||||||
CheckForFulfilledQuestsAction,
|
CheckForFulfilledQuestsAction,
|
||||||
HeroBackstoryUpdateActionGenerator,
|
HeroBackstoryUpdateActionGenerator,
|
||||||
ResolveBattleAction
|
ResolveBattleAction
|
||||||
}
|
}
|
||||||
import net.eagle0.eagle.library.actions.impl.command.{
|
import net.eagle0.eagle.library.actions.impl.command.{AvailableCommandTypeMap, CommandFactory}
|
||||||
AvailableCommandTypeMap,
|
import net.eagle0.eagle.library.actions.impl.common.{ActionWithResultingState, RandomStateProtoSequencer}
|
||||||
CommandFactory
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.actions.impl.common.{
|
|
||||||
ActionWithResultingState,
|
|
||||||
RandomStateProtoSequencer
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.hero_generator.HeroGenerator
|
import net.eagle0.eagle.library.util.hero_generator.HeroGenerator
|
||||||
import net.eagle0.eagle.library.util.validations.RuntimeValidator
|
import net.eagle0.eagle.library.util.validations.RuntimeValidator
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||||
@@ -83,11 +74,9 @@ object EngineImpl {
|
|||||||
.toVector,
|
.toVector,
|
||||||
battalions = eng.currentState.battalions.values.toVector
|
battalions = eng.currentState.battalions.values.toVector
|
||||||
.map(BattalionConverter.fromProto),
|
.map(BattalionConverter.fromProto),
|
||||||
getHero =
|
getHero = hid => eng.currentState.heroes.get(hid).map(HeroConverter.fromProto),
|
||||||
hid => eng.currentState.heroes.get(hid).map(HeroConverter.fromProto),
|
|
||||||
battalionTypes = eng.currentState.battalionTypes.toVector,
|
battalionTypes = eng.currentState.battalionTypes.toVector,
|
||||||
heroBackstoryTextIdLookup =
|
heroBackstoryTextIdLookup = hid => eng.currentState.heroes(hid).backstoryVersions.last.textId
|
||||||
hid => eng.currentState.heroes(hid).backstoryVersions.last.textId
|
|
||||||
).results
|
).results
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,8 +88,7 @@ object EngineImpl {
|
|||||||
withPhaseAdvancement(engineAndResults)
|
withPhaseAdvancement(engineAndResults)
|
||||||
)
|
)
|
||||||
|
|
||||||
if newEar.results.length > engineAndResults.results.length then
|
if newEar.results.length > engineAndResults.results.length then withUpdateChecks(newEar)
|
||||||
withUpdateChecks(newEar)
|
|
||||||
else newEar
|
else newEar
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +98,7 @@ object EngineImpl {
|
|||||||
): EngineAndResults = withUpdateChecks(
|
): EngineAndResults = withUpdateChecks(
|
||||||
EngineAndResultsImpl(
|
EngineAndResultsImpl(
|
||||||
engine = engine.copy(
|
engine = engine.copy(
|
||||||
currentState =
|
currentState = results.lastOption.map(_.gameState).getOrElse(engine.currentState),
|
||||||
results.lastOption.map(_.gameState).getOrElse(engine.currentState),
|
|
||||||
history = engine.history.withNewResults(results)
|
history = engine.history.withNewResults(results)
|
||||||
),
|
),
|
||||||
results = results.map(_.actionResult)
|
results = results.map(_.actionResult)
|
||||||
@@ -146,14 +133,14 @@ final case class EngineAndResultsImpl(
|
|||||||
|
|
||||||
def recursiveTransformT(
|
def recursiveTransformT(
|
||||||
f: EngineImpl => Vector[ActionResultT]
|
f: EngineImpl => Vector[ActionResultT]
|
||||||
): EngineAndResultsImpl = recursiveTransform(eng => {
|
): EngineAndResultsImpl = recursiveTransform { eng =>
|
||||||
val results = f(eng)
|
val results = f(eng)
|
||||||
RandomStateProtoSequencer(
|
RandomStateProtoSequencer(
|
||||||
initialStateProto = eng.currentState,
|
initialStateProto = eng.currentState,
|
||||||
actionResultProtoApplier = eng.actionResultProtoApplier,
|
actionResultProtoApplier = eng.actionResultProtoApplier,
|
||||||
functionalRandom = SeededRandom(eng.currentState.randomSeed)
|
functionalRandom = SeededRandom(eng.currentState.randomSeed)
|
||||||
).withActionResultTs(_ => results).results.newValue
|
).withActionResultTs(_ => results).results.newValue
|
||||||
})
|
}
|
||||||
|
|
||||||
def saveNow: EngineAndResultsImpl =
|
def saveNow: EngineAndResultsImpl =
|
||||||
this.copy(engine = this.engine.saveNow)
|
this.copy(engine = this.engine.saveNow)
|
||||||
@@ -169,8 +156,7 @@ case class EngineImpl(
|
|||||||
override val currentState: GameState,
|
override val currentState: GameState,
|
||||||
heroGenerator: HeroGenerator,
|
heroGenerator: HeroGenerator,
|
||||||
override val history: GameHistory,
|
override val history: GameHistory,
|
||||||
actionResultProtoApplier: ActionResultProtoApplier =
|
actionResultProtoApplier: ActionResultProtoApplier = new ActionResultProtoApplierImpl(validator = RuntimeValidator)
|
||||||
new ActionResultProtoApplierImpl(validator = RuntimeValidator)
|
|
||||||
) extends Engine {
|
) extends Engine {
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.commandRequire
|
import net.eagle0.eagle.library.util.EagleRequire.commandRequire
|
||||||
|
|
||||||
@@ -298,7 +284,7 @@ case class EngineImpl(
|
|||||||
availableCommandOpt.isDefined,
|
availableCommandOpt.isDefined,
|
||||||
s"No matching available command for selected command $selectedCommand"
|
s"No matching available command for selected command $selectedCommand"
|
||||||
)
|
)
|
||||||
val availableCommand = availableCommandOpt.get
|
val availableCommand = availableCommandOpt.get
|
||||||
|
|
||||||
val sequencer = RandomStateProtoSequencer(
|
val sequencer = RandomStateProtoSequencer(
|
||||||
initialStateProto = this.currentState,
|
initialStateProto = this.currentState,
|
||||||
@@ -326,9 +312,7 @@ case class EngineImpl(
|
|||||||
)
|
)
|
||||||
|
|
||||||
results
|
results
|
||||||
}.withProtolessSequentialResultsAction(gs =>
|
}.withProtolessSequentialResultsAction(gs => HeroBackstoryUpdateActionGenerator.fromGameState(gs))
|
||||||
HeroBackstoryUpdateActionGenerator.fromGameState(gs)
|
|
||||||
)
|
|
||||||
|
|
||||||
appliedResults(
|
appliedResults(
|
||||||
engine = this,
|
engine = this,
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ trait GameHistory {
|
|||||||
def all: Vector[ActionWithResultingState]
|
def all: Vector[ActionWithResultingState]
|
||||||
def since(count: Int): Vector[ActionWithResultingState]
|
def since(count: Int): Vector[ActionWithResultingState]
|
||||||
def sinceCapped(count: Int, resultSizeCap: Int): CappedResults =
|
def sinceCapped(count: Int, resultSizeCap: Int): CappedResults =
|
||||||
if this.count - count <= resultSizeCap then
|
if this.count - count <= resultSizeCap then CappedResults(since(count), None)
|
||||||
CappedResults(since(count), None)
|
|
||||||
else
|
else
|
||||||
CappedResults(
|
CappedResults(
|
||||||
since(this.count - resultSizeCap),
|
since(this.count - resultSizeCap),
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ import net.eagle0.common.SeededRandom
|
|||||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||||
import net.eagle0.eagle.common.round_phase.RoundPhase.*
|
import net.eagle0.eagle.common.round_phase.RoundPhase.*
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.actions.applier.{
|
import net.eagle0.eagle.library.actions.applier.{ActionResultProtoApplier, ActionResultTApplierImpl}
|
||||||
ActionResultProtoApplier,
|
|
||||||
ActionResultTApplierImpl
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.actions.availability.AvailableCommandsFactory
|
import net.eagle0.eagle.library.actions.availability.AvailableCommandsFactory
|
||||||
import net.eagle0.eagle.library.actions.impl.action.*
|
import net.eagle0.eagle.library.actions.impl.action.*
|
||||||
import net.eagle0.eagle.library.actions.impl.command.CommandFactory
|
import net.eagle0.eagle.library.actions.impl.command.CommandFactory
|
||||||
@@ -29,8 +26,8 @@ import net.eagle0.eagle.RoundId
|
|||||||
object RoundPhaseAdvancer {
|
object RoundPhaseAdvancer {
|
||||||
private val times: mutable.Map[RoundPhase, Long] =
|
private val times: mutable.Map[RoundPhase, Long] =
|
||||||
mutable.Map(RoundPhase.values.map(_ -> 0L)*)
|
mutable.Map(RoundPhase.values.map(_ -> 0L)*)
|
||||||
private val print = false
|
private val print = false
|
||||||
private val roundsBetweenPrint = 100
|
private val roundsBetweenPrint = 100
|
||||||
|
|
||||||
private def printTimings(roundId: RoundId): Unit = {
|
private def printTimings(roundId: RoundId): Unit = {
|
||||||
val totalTime = times.values.sum.toDouble / 1000.0
|
val totalTime = times.values.sum.toDouble / 1000.0
|
||||||
@@ -39,7 +36,7 @@ object RoundPhaseAdvancer {
|
|||||||
case (phase, time) =>
|
case (phase, time) =>
|
||||||
val timeInSecs = time.toDouble / 1000.0
|
val timeInSecs = time.toDouble / 1000.0
|
||||||
val msPerRound = time.toDouble / roundId.toDouble
|
val msPerRound = time.toDouble / roundId.toDouble
|
||||||
val timePct = timeInSecs / totalTime * 100.0
|
val timePct = timeInSecs / totalTime * 100.0
|
||||||
println(
|
println(
|
||||||
f"$phase%-25s: $timeInSecs%6.2fs ($msPerRound%4.2fms per round), $timePct%4.1f%%"
|
f"$phase%-25s: $timeInSecs%6.2fs ($msPerRound%4.2fms per round), $timePct%4.1f%%"
|
||||||
)
|
)
|
||||||
@@ -56,7 +53,7 @@ object RoundPhaseAdvancer {
|
|||||||
commandFactory: CommandFactory
|
commandFactory: CommandFactory
|
||||||
): Vector[ActionWithResultingState] = {
|
): Vector[ActionWithResultingState] = {
|
||||||
val currentPhase = currentState.currentPhase
|
val currentPhase = currentState.currentPhase
|
||||||
val startTime = System.currentTimeMillis
|
val startTime = System.currentTimeMillis
|
||||||
|
|
||||||
if print && currentPhase == NEW_ROUND && currentState.currentRoundId % roundsBetweenPrint == 0
|
if print && currentPhase == NEW_ROUND && currentState.currentRoundId % roundsBetweenPrint == 0
|
||||||
then {
|
then {
|
||||||
@@ -270,8 +267,7 @@ object RoundPhaseAdvancer {
|
|||||||
currentRoundId = currentState.currentRoundId,
|
currentRoundId = currentState.currentRoundId,
|
||||||
currentDate = DateConverter.fromProto(currentState.currentDate),
|
currentDate = DateConverter.fromProto(currentState.currentDate),
|
||||||
battleCounter = currentState.battleCounter,
|
battleCounter = currentState.battleCounter,
|
||||||
heroes =
|
heroes = currentState.heroes.view.mapValues(HeroConverter.fromProto).toMap,
|
||||||
currentState.heroes.view.mapValues(HeroConverter.fromProto).toMap,
|
|
||||||
battalions = currentState.battalions.view
|
battalions = currentState.battalions.view
|
||||||
.mapValues(BattalionConverter.fromProto)
|
.mapValues(BattalionConverter.fromProto)
|
||||||
.toMap,
|
.toMap,
|
||||||
@@ -286,7 +282,7 @@ object RoundPhaseAdvancer {
|
|||||||
converted.typeId -> converted
|
converted.typeId -> converted
|
||||||
}.toMap
|
}.toMap
|
||||||
)
|
)
|
||||||
val requestResults = actionResultProtoApplier.applyActionResults(
|
val requestResults = actionResultProtoApplier.applyActionResults(
|
||||||
currentState,
|
currentState,
|
||||||
requestBattlesAction.results.map(
|
requestBattlesAction.results.map(
|
||||||
ActionResultProtoConverter.toProto(_)
|
ActionResultProtoConverter.toProto(_)
|
||||||
@@ -337,8 +333,7 @@ object RoundPhaseAdvancer {
|
|||||||
currentState,
|
currentState,
|
||||||
EndDiplomacyResolutionPhaseAction(
|
EndDiplomacyResolutionPhaseAction(
|
||||||
currentState,
|
currentState,
|
||||||
actionResultTApplier =
|
actionResultTApplier = ActionResultTApplierImpl(actionResultProtoApplier)
|
||||||
ActionResultTApplierImpl(actionResultProtoApplier)
|
|
||||||
).randomResults(SeededRandom(currentState.randomSeed))
|
).randomResults(SeededRandom(currentState.randomSeed))
|
||||||
.newValue
|
.newValue
|
||||||
.map(ActionResultProtoConverter.toProto)
|
.map(ActionResultProtoConverter.toProto)
|
||||||
@@ -352,7 +347,7 @@ object RoundPhaseAdvancer {
|
|||||||
case Unrecognized(x) =>
|
case Unrecognized(x) =>
|
||||||
throw new IllegalStateException(s"Unknown round phase $x")
|
throw new IllegalStateException(s"Unknown round phase $x")
|
||||||
}
|
}
|
||||||
val timeSpent = System.currentTimeMillis - startTime
|
val timeSpent = System.currentTimeMillis - startTime
|
||||||
times(currentPhase) = times(currentPhase) + timeSpent
|
times(currentPhase) = times(currentPhase) + timeSpent
|
||||||
|
|
||||||
validateResults(results, currentState, availableCommandsFactory)
|
validateResults(results, currentState, availableCommandsFactory)
|
||||||
|
|||||||
+130
-157
@@ -31,10 +31,7 @@ import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.{
|
|||||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||||
import net.eagle0.eagle.internal.army.{HostileArmyGroup, MovingArmy}
|
import net.eagle0.eagle.internal.army.{HostileArmyGroup, MovingArmy}
|
||||||
import net.eagle0.eagle.internal.battalion.Battalion
|
import net.eagle0.eagle.internal.battalion.Battalion
|
||||||
import net.eagle0.eagle.internal.changed_faction.{
|
import net.eagle0.eagle.internal.changed_faction.{ChangedFaction, TrustLevelUpdate}
|
||||||
ChangedFaction,
|
|
||||||
TrustLevelUpdate
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero
|
import net.eagle0.eagle.internal.changed_hero.ChangedHero
|
||||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero.{Loyalty, Vigor}
|
import net.eagle0.eagle.internal.changed_hero.ChangedHero.{Loyalty, Vigor}
|
||||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||||
@@ -49,24 +46,19 @@ import net.eagle0.eagle.internal.province.Province
|
|||||||
import net.eagle0.eagle.internal.run_status.RunStatus
|
import net.eagle0.eagle.internal.run_status.RunStatus
|
||||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle
|
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle
|
||||||
import net.eagle0.eagle.library.actions.impl.common.ActionWithResultingState
|
import net.eagle0.eagle.library.actions.impl.common.ActionWithResultingState
|
||||||
import net.eagle0.eagle.library.settings.{
|
import net.eagle0.eagle.library.settings.{ExtraXpForStatBumpOver100, XpForStatBump}
|
||||||
ExtraXpForStatBumpOver100,
|
|
||||||
XpForStatBump
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||||
import net.eagle0.eagle.library.util.validations.Validator
|
import net.eagle0.eagle.library.util.validations.Validator
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||||
import net.eagle0.eagle.library.util.ProvinceEventUtils
|
import net.eagle0.eagle.library.util.ProvinceEventUtils
|
||||||
import net.eagle0.eagle.library.EagleInternalException
|
import net.eagle0.eagle.library.EagleInternalException
|
||||||
|
|
||||||
class ActionResultProtoApplierImpl(validator: Validator)
|
class ActionResultProtoApplierImpl(validator: Validator) extends ActionResultProtoApplier {
|
||||||
extends ActionResultProtoApplier {
|
|
||||||
import validator.validate
|
import validator.validate
|
||||||
|
|
||||||
override def xpForStatBump(stat: Int): Int =
|
override def xpForStatBump(stat: Int): Int =
|
||||||
if stat <= 99 then XpForStatBump.intValue
|
if stat <= 99 then XpForStatBump.intValue
|
||||||
else
|
else XpForStatBump.intValue + ExtraXpForStatBumpOver100.intValue * (stat - 99)
|
||||||
XpForStatBump.intValue + ExtraXpForStatBumpOver100.intValue * (stat - 99)
|
|
||||||
|
|
||||||
private val trustMax: Int = 100
|
private val trustMax: Int = 100
|
||||||
|
|
||||||
@@ -93,10 +85,11 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
if changedProvinces.isEmpty then gameState
|
if changedProvinces.isEmpty then gameState
|
||||||
else
|
else
|
||||||
changedProvinces
|
changedProvinces
|
||||||
.foldLeft(gameState) { case (gs, cp) =>
|
.foldLeft(gameState) {
|
||||||
val after = gs.applyChangedProvince(cp)
|
case (gs, cp) =>
|
||||||
validate(after.provinces(cp.id), gs.currentPhase)
|
val after = gs.applyChangedProvince(cp)
|
||||||
after
|
validate(after.provinces(cp.id), gs.currentPhase)
|
||||||
|
after
|
||||||
}
|
}
|
||||||
|
|
||||||
def applyChangedProvince(cp: ChangedProvince): GameState = {
|
def applyChangedProvince(cp: ChangedProvince): GameState = {
|
||||||
@@ -134,7 +127,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
_.incomingShipments.modify { is =>
|
_.incomingShipments.modify { is =>
|
||||||
val holdovers =
|
val holdovers =
|
||||||
is.filterNot(sh => cp.removedIncomingShipmentIds.contains(sh.id))
|
is.filterNot(sh => cp.removedIncomingShipmentIds.contains(sh.id))
|
||||||
val nextId = holdovers
|
val nextId = holdovers
|
||||||
.map(_.id)
|
.map(_.id)
|
||||||
.reduceOption(_ max _)
|
.reduceOption(_ max _)
|
||||||
.getOrElse(0) + 1
|
.getOrElse(0) + 1
|
||||||
@@ -149,9 +142,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
da.map { nda =>
|
da.map { nda =>
|
||||||
nda.withUnits(
|
nda.withUnits(
|
||||||
nda.units
|
nda.units
|
||||||
.filterNot(u =>
|
.filterNot(u => cp.removedRulingPlayerHeroIds.contains(u.heroId))
|
||||||
cp.removedRulingPlayerHeroIds.contains(u.heroId)
|
|
||||||
)
|
|
||||||
.map {
|
.map {
|
||||||
case CombatUnit(
|
case CombatUnit(
|
||||||
pid,
|
pid,
|
||||||
@@ -185,17 +176,11 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
newF
|
newF
|
||||||
},
|
},
|
||||||
_.priceIndex.setIfDefined(cp.newPriceIndex),
|
_.priceIndex.setIfDefined(cp.newPriceIndex),
|
||||||
_.economy.modify(e =>
|
_.economy.modify(e => (e + cp.economyDelta.getOrElse(0.0)).max(0.0).min(100.0)),
|
||||||
(e + cp.economyDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
|
||||||
),
|
|
||||||
_.agriculture
|
_.agriculture
|
||||||
.modify(a =>
|
.modify(a => (a + cp.agricultureDelta.getOrElse(0.0)).max(0.0).min(100.0)),
|
||||||
(a + cp.agricultureDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
|
||||||
),
|
|
||||||
_.infrastructure
|
_.infrastructure
|
||||||
.modify(i =>
|
.modify(i => (i + cp.infrastructureDelta.getOrElse(0.0)).max(0.0).min(100.0)),
|
||||||
(i + cp.infrastructureDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
|
||||||
),
|
|
||||||
_.economyDevastation.modify(d =>
|
_.economyDevastation.modify(d =>
|
||||||
(d + cp.economyDevastationDelta.getOrElse(0.0))
|
(d + cp.economyDevastationDelta.getOrElse(0.0))
|
||||||
.max(0.0)
|
.max(0.0)
|
||||||
@@ -211,16 +196,12 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
.max(0.0)
|
.max(0.0)
|
||||||
.min(provinceBefore.infrastructure)
|
.min(provinceBefore.infrastructure)
|
||||||
),
|
),
|
||||||
_.support.modify(s =>
|
_.support.modify(s => (s + cp.supportDelta.getOrElse(0.0)).max(0.0).min(100.0)),
|
||||||
(s + cp.supportDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
|
||||||
),
|
|
||||||
_.hasActed.setIfDefined(cp.setHasActed),
|
_.hasActed.setIfDefined(cp.setHasActed),
|
||||||
_.rulerIsTraveling.setIfDefined(cp.setRulerIsTraveling),
|
_.rulerIsTraveling.setIfDefined(cp.setRulerIsTraveling),
|
||||||
_.unaffiliatedHeroes.modify(uhs =>
|
_.unaffiliatedHeroes.modify(uhs =>
|
||||||
uhs
|
uhs
|
||||||
.filterNot(uh =>
|
.filterNot(uh => cp.removedUnaffiliatedHeroIds.contains(uh.heroId))
|
||||||
cp.removedUnaffiliatedHeroIds.contains(uh.heroId)
|
|
||||||
)
|
|
||||||
.filterNot(uh =>
|
.filterNot(uh =>
|
||||||
cp.changedUnaffiliatedHeroes
|
cp.changedUnaffiliatedHeroes
|
||||||
.map(_.heroId)
|
.map(_.heroId)
|
||||||
@@ -247,14 +228,14 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
cp.newProvinceEvents.exists(
|
cp.newProvinceEvents.exists(
|
||||||
_.newEvents.exists(ProvinceEventUtils.isBeastsEvent)
|
_.newEvents.exists(ProvinceEventUtils.isBeastsEvent)
|
||||||
)
|
)
|
||||||
) { gameState.currentDate.get }
|
)(gameState.currentDate.get)
|
||||||
),
|
),
|
||||||
_.lastRiotDate.setIfDefined(
|
_.lastRiotDate.setIfDefined(
|
||||||
Option.when(
|
Option.when(
|
||||||
cp.newProvinceEvents.exists(
|
cp.newProvinceEvents.exists(
|
||||||
_.newEvents.exists(ProvinceEventUtils.isImminentRiotEvent)
|
_.newEvents.exists(ProvinceEventUtils.isImminentRiotEvent)
|
||||||
)
|
)
|
||||||
) { gameState.currentDate.get }
|
)(gameState.currentDate.get)
|
||||||
),
|
),
|
||||||
_.incomingEndTurnActions :++= cp.addedIncomingEndTurnActions,
|
_.incomingEndTurnActions :++= cp.addedIncomingEndTurnActions,
|
||||||
_.incomingEndTurnActions.modify(
|
_.incomingEndTurnActions.modify(
|
||||||
@@ -265,9 +246,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
.map(idx => dcs.patch(idx, Nil, 1))
|
.map(idx => dcs.patch(idx, Nil, 1))
|
||||||
.getOrElse(dcs) ++ cp.addedDeferredChange.asNonEmpty
|
.getOrElse(dcs) ++ cp.addedDeferredChange.asNonEmpty
|
||||||
),
|
),
|
||||||
_.battleRevelations.modify(brs =>
|
_.battleRevelations.modify(brs => brs.diff(cp.removedBattleRevelations) ++ cp.addedBattleRevelations)
|
||||||
brs.diff(cp.removedBattleRevelations) ++ cp.addedBattleRevelations
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val provinceFixedForRuler =
|
val provinceFixedForRuler =
|
||||||
@@ -275,27 +254,27 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
then
|
then
|
||||||
provinceApplied.update(
|
provinceApplied.update(
|
||||||
_.optionalRulingFactionId := None,
|
_.optionalRulingFactionId := None,
|
||||||
_.optionalRulingHeroId := None,
|
_.optionalRulingHeroId := None,
|
||||||
_.rulingFactionHeroIds := Vector.empty,
|
_.rulingFactionHeroIds := Vector.empty,
|
||||||
_.support := 0,
|
_.support := 0,
|
||||||
_.provinceOrders := ProvinceOrderType.UNKNOWN_ORDERS,
|
_.provinceOrders := ProvinceOrderType.UNKNOWN_ORDERS,
|
||||||
_.unaffiliatedHeroes.modify(uhs =>
|
_.unaffiliatedHeroes.modify(uhs =>
|
||||||
uhs.map(uh =>
|
uhs.map(uh =>
|
||||||
uh.update(
|
uh.update(
|
||||||
_.recruitmentInfo := RecruitmentInfo(
|
_.recruitmentInfo := RecruitmentInfo(
|
||||||
status = uh.`type` match {
|
status = uh.`type` match {
|
||||||
case UNAFFILIATED_HERO_PRISONER =>
|
case UNAFFILIATED_HERO_PRISONER =>
|
||||||
RECRUITMENT_STATUS_PRISONER
|
RECRUITMENT_STATUS_PRISONER
|
||||||
case UNAFFILIATED_HERO_MOVING_PRISONER =>
|
case UNAFFILIATED_HERO_MOVING_PRISONER =>
|
||||||
RECRUITMENT_STATUS_MOVING_PRISONER
|
RECRUITMENT_STATUS_MOVING_PRISONER
|
||||||
case UNAFFILIATED_HERO_RETURNING_PRISONER =>
|
case UNAFFILIATED_HERO_RETURNING_PRISONER =>
|
||||||
RECRUITMENT_STATUS_NO_RULER_IN_PROVINCE
|
RECRUITMENT_STATUS_NO_RULER_IN_PROVINCE
|
||||||
case UNAFFILIATED_HERO_OUTLAW => RECRUITMENT_STATUS_OUTLAW
|
case UNAFFILIATED_HERO_OUTLAW => RECRUITMENT_STATUS_OUTLAW
|
||||||
case UNAFFILIATED_HERO_TRAVELER =>
|
case UNAFFILIATED_HERO_TRAVELER =>
|
||||||
RECRUITMENT_STATUS_TRAVELER
|
RECRUITMENT_STATUS_TRAVELER
|
||||||
case UNAFFILIATED_HERO_RESIDENT =>
|
case UNAFFILIATED_HERO_RESIDENT =>
|
||||||
RECRUITMENT_STATUS_NO_RULER_IN_PROVINCE
|
RECRUITMENT_STATUS_NO_RULER_IN_PROVINCE
|
||||||
case UNAFFILIATED_HERO_UNKNOWN =>
|
case UNAFFILIATED_HERO_UNKNOWN =>
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
"Unknown unaffiliated hero type"
|
"Unknown unaffiliated hero type"
|
||||||
)
|
)
|
||||||
@@ -325,12 +304,13 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
): Vector[MovingArmy] = {
|
): Vector[MovingArmy] = {
|
||||||
val holdovers =
|
val holdovers =
|
||||||
existingArmies.filterNot(ma => removedIds.contains(ma.id))
|
existingArmies.filterNot(ma => removedIds.contains(ma.id))
|
||||||
val nextId = holdovers
|
val nextId = holdovers
|
||||||
.map(_.id)
|
.map(_.id)
|
||||||
.reduceOption(_ max _)
|
.reduceOption(_ max _)
|
||||||
.getOrElse(0) + 1
|
.getOrElse(0) + 1
|
||||||
holdovers.toVector ++ addedArmies.zipWithIndex.map { case (army, index) =>
|
holdovers.toVector ++ addedArmies.zipWithIndex.map {
|
||||||
army.withId(index + nextId)
|
case (army, index) =>
|
||||||
|
army.withId(index + nextId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +319,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
removedFactionIds: Seq[FactionId],
|
removedFactionIds: Seq[FactionId],
|
||||||
addedArmies: Seq[HostileArmyGroup],
|
addedArmies: Seq[HostileArmyGroup],
|
||||||
statusChanges: Seq[HostileArmyGroupStatusChange]
|
statusChanges: Seq[HostileArmyGroupStatusChange]
|
||||||
): Vector[HostileArmyGroup] = {
|
): Vector[HostileArmyGroup] =
|
||||||
statusChanges
|
statusChanges
|
||||||
.foldLeft(
|
.foldLeft(
|
||||||
existingGroups
|
existingGroups
|
||||||
@@ -360,9 +340,8 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.toVector ++ addedArmies
|
.toVector ++ addedArmies
|
||||||
}
|
|
||||||
|
|
||||||
def applyProvinceActed(actedProvince: Option[Int]): GameState = {
|
def applyProvinceActed(actedProvince: Option[Int]): GameState = {
|
||||||
internalRequire(
|
internalRequire(
|
||||||
!actedProvince.contains(0),
|
!actedProvince.contains(0),
|
||||||
"Province acted has id 0"
|
"Province acted has id 0"
|
||||||
@@ -396,20 +375,19 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
): GameState =
|
): GameState =
|
||||||
if newBattalions.isEmpty then gameState
|
if newBattalions.isEmpty then gameState
|
||||||
else
|
else
|
||||||
provinceId
|
provinceId.map { pid =>
|
||||||
.map { pid =>
|
val maxCurrentId =
|
||||||
val maxCurrentId =
|
if gameState.battalions.isEmpty then 0
|
||||||
if gameState.battalions.isEmpty then 0
|
else gameState.battalions.keys.max
|
||||||
else gameState.battalions.keys.max
|
val newIds =
|
||||||
val newIds =
|
(maxCurrentId + 1) to (maxCurrentId + newBattalions.size)
|
||||||
(maxCurrentId + 1) to (maxCurrentId + newBattalions.size)
|
gameState.update(
|
||||||
gameState.update(
|
_.battalions :++= newIds.zip(newBattalions).map {
|
||||||
_.battalions :++= newIds.zip(newBattalions).map {
|
case (id, batt) => id -> batt.withId(id)
|
||||||
case (id, batt) => id -> batt.withId(id)
|
},
|
||||||
},
|
_.provinces(pid).battalionIds :++= newIds
|
||||||
_.provinces(pid).battalionIds :++= newIds
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
.getOrElse(
|
.getOrElse(
|
||||||
gameState
|
gameState
|
||||||
.update(_.battalions :++= newBattalions.map(b => b.id -> b))
|
.update(_.battalions :++= newBattalions.map(b => b.id -> b))
|
||||||
@@ -444,14 +422,15 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
_.battalions := gameState.battalions -- destroyedBattalionIds,
|
_.battalions := gameState.battalions -- destroyedBattalionIds,
|
||||||
_.destroyedBattalions :++= destroyedBattalionIds
|
_.destroyedBattalions :++= destroyedBattalionIds
|
||||||
.filterNot(_ == -1)
|
.filterNot(_ == -1)
|
||||||
.map { bid => bid -> gameState.battalions(bid) },
|
.map(bid => bid -> gameState.battalions(bid)),
|
||||||
_.provinces := gameState.provinces.map { case (pid, p) =>
|
_.provinces := gameState.provinces.map {
|
||||||
pid -> p.update(
|
case (pid, p) =>
|
||||||
_.battalionIds := p.battalionIds
|
pid -> p.update(
|
||||||
.filterNot(destroyedBattalionIds.contains),
|
_.battalionIds := p.battalionIds
|
||||||
_.incomingArmies := p.incomingArmies
|
.filterNot(destroyedBattalionIds.contains),
|
||||||
.map(a => movingArmyWithoutBattalions(a, destroyedBattalionIds))
|
_.incomingArmies := p.incomingArmies
|
||||||
)
|
.map(a => movingArmyWithoutBattalions(a, destroyedBattalionIds))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -484,14 +463,14 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
def applyChangedHeroes(
|
def applyChangedHeroes(
|
||||||
changedHeroes: Seq[ChangedHero],
|
changedHeroes: Seq[ChangedHero],
|
||||||
date: Date
|
date: Date
|
||||||
): GameState = {
|
): GameState =
|
||||||
if changedHeroes.isEmpty then gameState
|
if changedHeroes.isEmpty then gameState
|
||||||
else
|
else
|
||||||
changedHeroes
|
changedHeroes
|
||||||
.foldLeft(gameState) { case (gs, ch) =>
|
.foldLeft(gameState) {
|
||||||
gs.applyChangedHero(ch, date)
|
case (gs, ch) =>
|
||||||
|
gs.applyChangedHero(ch, date)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private def statBump(
|
private def statBump(
|
||||||
stat: Int,
|
stat: Int,
|
||||||
@@ -633,9 +612,10 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def applyRemovedFactions(removedFactionIds: Set[FactionId]): GameState = {
|
def applyRemovedFactions(removedFactionIds: Set[FactionId]): GameState =
|
||||||
gameState.factions.partition { case (fid, _) =>
|
gameState.factions.partition {
|
||||||
removedFactionIds(fid)
|
case (fid, _) =>
|
||||||
|
removedFactionIds(fid)
|
||||||
} match {
|
} match {
|
||||||
case (removed, remaining) =>
|
case (removed, remaining) =>
|
||||||
gameState.update(
|
gameState.update(
|
||||||
@@ -643,16 +623,17 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
_.factions := remaining
|
_.factions := remaining
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def applyRemovedHeroes(removedHeroIds: Set[HeroId]): GameState =
|
def applyRemovedHeroes(removedHeroIds: Set[HeroId]): GameState =
|
||||||
gameState.heroes.partition { case (k, _) =>
|
gameState.heroes.partition {
|
||||||
removedHeroIds(k)
|
case (k, _) =>
|
||||||
|
removedHeroIds(k)
|
||||||
} match {
|
} match {
|
||||||
case (removed, remaining) =>
|
case (removed, remaining) =>
|
||||||
gameState.update(
|
gameState.update(
|
||||||
_.killedHeroes :++= removed.map { case (hid, hero) =>
|
_.killedHeroes :++= removed.map {
|
||||||
(hid, hero.clearFactionId)
|
case (hid, hero) =>
|
||||||
|
(hid, hero.clearFactionId)
|
||||||
},
|
},
|
||||||
_.heroes := remaining
|
_.heroes := remaining
|
||||||
)
|
)
|
||||||
@@ -670,12 +651,11 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
actionResultType: ActionResultType,
|
actionResultType: ActionResultType,
|
||||||
notification: Option[Notification]
|
notification: Option[Notification]
|
||||||
): GameState =
|
): GameState =
|
||||||
notification
|
notification.map { note =>
|
||||||
.map { note =>
|
gameState.update(
|
||||||
gameState.update(
|
_.deferredNotifications :+= note
|
||||||
_.deferredNotifications :+= note
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
.getOrElse(gameState)
|
.getOrElse(gameState)
|
||||||
|
|
||||||
def applyRemovedNotifications(
|
def applyRemovedNotifications(
|
||||||
@@ -727,36 +707,34 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
)
|
)
|
||||||
) ++ cf.updatedReconnedProvinces
|
) ++ cf.updatedReconnedProvinces
|
||||||
},
|
},
|
||||||
_.factionRelationships
|
_.factionRelationships.modify { (relationships: Seq[FactionRelationship]) =>
|
||||||
.modify { (relationships: Seq[FactionRelationship]) =>
|
cf.trustLevelUpdates
|
||||||
cf.trustLevelUpdates
|
.foldLeft(relationships) {
|
||||||
.foldLeft(relationships) {
|
(
|
||||||
(
|
relationships: Seq[FactionRelationship],
|
||||||
relationships: Seq[FactionRelationship],
|
update: TrustLevelUpdate
|
||||||
update: TrustLevelUpdate
|
) =>
|
||||||
) =>
|
val existingRelationship =
|
||||||
val existingRelationship =
|
relationships
|
||||||
relationships
|
.find(
|
||||||
.find(
|
_.targetFactionId == update.targetFactionId
|
||||||
_.targetFactionId == update.targetFactionId
|
)
|
||||||
|
.getOrElse(
|
||||||
|
FactionRelationship(
|
||||||
|
targetFactionId = update.targetFactionId,
|
||||||
|
relationshipLevel = FactionRelationship.RelationshipLevel.HOSTILE,
|
||||||
|
trustValue = 0
|
||||||
)
|
)
|
||||||
.getOrElse(
|
)
|
||||||
FactionRelationship(
|
val newValue = Math.min(
|
||||||
targetFactionId = update.targetFactionId,
|
trustMax,
|
||||||
relationshipLevel =
|
update.delta + existingRelationship.trustValue
|
||||||
FactionRelationship.RelationshipLevel.HOSTILE,
|
)
|
||||||
trustValue = 0
|
relationships.filterNot(
|
||||||
)
|
_.targetFactionId == update.targetFactionId
|
||||||
)
|
) :+ existingRelationship.withTrustValue(newValue)
|
||||||
val newValue = Math.min(
|
}
|
||||||
trustMax,
|
},
|
||||||
update.delta + existingRelationship.trustValue
|
|
||||||
)
|
|
||||||
relationships.filterNot(
|
|
||||||
_.targetFactionId == update.targetFactionId
|
|
||||||
) :+ existingRelationship.withTrustValue(newValue)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_.lastActedProvinceIdThisRound.modify { lpid =>
|
_.lastActedProvinceIdThisRound.modify { lpid =>
|
||||||
if cf.clearLastActedProvinceId then 0
|
if cf.clearLastActedProvinceId then 0
|
||||||
else cf.newLastActedProvinceId.getOrElse(lpid)
|
else cf.newLastActedProvinceId.getOrElse(lpid)
|
||||||
@@ -835,58 +813,53 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
)
|
)
|
||||||
|
|
||||||
def applyNewBattle(battle: Option[ShardokBattle]): GameState =
|
def applyNewBattle(battle: Option[ShardokBattle]): GameState =
|
||||||
battle
|
battle.map { b =>
|
||||||
.map { b =>
|
gameState.update(
|
||||||
gameState.update(
|
_.outstandingBattles.modify(_ :+ b),
|
||||||
_.outstandingBattles.modify(_ :+ b),
|
_.battleCounter.modify(_.max(b.battleIndex))
|
||||||
_.battleCounter.modify(_.max(b.battleIndex))
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
.getOrElse(gameState)
|
.getOrElse(gameState)
|
||||||
|
|
||||||
def applyResolvedBattle(shardokGameId: Option[ShardokGameId]): GameState =
|
def applyResolvedBattle(shardokGameId: Option[ShardokGameId]): GameState =
|
||||||
gameState
|
gameState
|
||||||
.withOutstandingBattles(
|
.withOutstandingBattles(
|
||||||
gameState.outstandingBattles.filterNot(batt =>
|
gameState.outstandingBattles.filterNot(batt => shardokGameId.contains(batt.shardokGameId))
|
||||||
shardokGameId.contains(batt.shardokGameId)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def applyNewSeed(newSeed: Option[Long]): GameState =
|
def applyNewSeed(newSeed: Option[Long]): GameState =
|
||||||
newSeed.map { s => gameState.withRandomSeed(s) }.getOrElse(gameState)
|
newSeed.map(s => gameState.withRandomSeed(s)).getOrElse(gameState)
|
||||||
|
|
||||||
def applyChronicleEntry(
|
def applyChronicleEntry(
|
||||||
chronicleEntry: Option[ChronicleEntry]
|
chronicleEntry: Option[ChronicleEntry]
|
||||||
): GameState =
|
): GameState =
|
||||||
chronicleEntry
|
chronicleEntry.map { ce =>
|
||||||
.map { ce =>
|
gameState.update(
|
||||||
gameState.update(
|
_.chronicleEntries.modify { ces =>
|
||||||
_.chronicleEntries.modify { ces =>
|
ces.indexWhere(_.date == ce.date) match {
|
||||||
ces.indexWhere(_.date == ce.date) match {
|
case -1 => ces :+ ce
|
||||||
case -1 => ces :+ ce
|
case idx => ces.updated(idx, ce)
|
||||||
case idx => ces.updated(idx, ce)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
|
}
|
||||||
.getOrElse(gameState)
|
.getOrElse(gameState)
|
||||||
|
|
||||||
def applyCommandCountUpdate(actingFactionId: Option[FactionId]): GameState =
|
def applyCommandCountUpdate(actingFactionId: Option[FactionId]): GameState =
|
||||||
actingFactionId
|
actingFactionId.map { factionId =>
|
||||||
.map { factionId =>
|
val existingCount =
|
||||||
val existingCount =
|
gameState.factionCommandCounts.getOrElse(factionId, 0)
|
||||||
gameState.factionCommandCounts.getOrElse(factionId, 0)
|
gameState.update(
|
||||||
gameState.update(
|
_.factionCommandCounts(factionId) := existingCount + 1
|
||||||
_.factionCommandCounts(factionId) := existingCount + 1
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
.getOrElse(gameState)
|
.getOrElse(gameState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def applyActionResults(
|
override def applyActionResults(
|
||||||
startingState: GameState,
|
startingState: GameState,
|
||||||
results: Iterable[ActionResult]
|
results: Iterable[ActionResult]
|
||||||
): Vector[ActionWithResultingState] = {
|
): Vector[ActionWithResultingState] =
|
||||||
results
|
results
|
||||||
.foldLeft((startingState, Vector.empty[ActionWithResultingState])) {
|
.foldLeft((startingState, Vector.empty[ActionWithResultingState])) {
|
||||||
case ((gameState, acc), result) =>
|
case ((gameState, acc), result) =>
|
||||||
@@ -896,7 +869,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
._2
|
._2
|
||||||
}.toVector
|
.toVector
|
||||||
|
|
||||||
override def applyActionResult(
|
override def applyActionResult(
|
||||||
startingState: GameState,
|
startingState: GameState,
|
||||||
|
|||||||
+7
-7
@@ -13,8 +13,7 @@ object ActionResultTApplierImpl {
|
|||||||
new ActionResultTApplierImpl(protoApplier)
|
new ActionResultTApplierImpl(protoApplier)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActionResultTApplierImpl(protoApplier: ActionResultProtoApplier)
|
class ActionResultTApplierImpl(protoApplier: ActionResultProtoApplier) extends ActionResultTApplier {
|
||||||
extends ActionResultTApplier {
|
|
||||||
override def xpForStatBump(stat: Int): Int = protoApplier.xpForStatBump(stat)
|
override def xpForStatBump(stat: Int): Int = protoApplier.xpForStatBump(stat)
|
||||||
|
|
||||||
override def applyActionResults(
|
override def applyActionResults(
|
||||||
@@ -26,11 +25,12 @@ class ActionResultTApplierImpl(protoApplier: ActionResultProtoApplier)
|
|||||||
results.map(ActionResultProtoConverter.toProto)
|
results.map(ActionResultProtoConverter.toProto)
|
||||||
)
|
)
|
||||||
.zip(results)
|
.zip(results)
|
||||||
.map { case (actionWithResultingState, actionResult) =>
|
.map {
|
||||||
ActionResultTWithResultingState(
|
case (actionWithResultingState, actionResult) =>
|
||||||
actionResult = actionResult,
|
ActionResultTWithResultingState(
|
||||||
resultingState = actionWithResultingState.gameState
|
actionResult = actionResult,
|
||||||
)
|
resultingState = actionWithResultingState.gameState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def applyActionResult(
|
override def applyActionResult(
|
||||||
|
|||||||
+3
-6
@@ -12,24 +12,21 @@ object AvailableAlmsCommandFactory extends AvailableCommandsFactoryForType {
|
|||||||
.rulingFactionHeroIds
|
.rulingFactionHeroIds
|
||||||
.toVector
|
.toVector
|
||||||
.filter(hid => gs.heroes(hid).vigor >= MinVigorForAlms.doubleValue)
|
.filter(hid => gs.heroes(hid).vigor >= MinVigorForAlms.doubleValue)
|
||||||
.sortBy(hid =>
|
.sortBy(hid => (!gs.heroes(hid).profession.isPaladin, -gs.heroes(hid).vigor))
|
||||||
(!gs.heroes(hid).profession.isPaladin, -gs.heroes(hid).vigor)
|
|
||||||
)
|
|
||||||
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[AlmsAvailableCommand] = {
|
): Option[AlmsAvailableCommand] = {
|
||||||
val hids = availableHeroIds(gameState, provinceId)
|
val hids = availableHeroIds(gameState, provinceId)
|
||||||
val province = gameState.provinces(provinceId)
|
val province = gameState.provinces(provinceId)
|
||||||
|
|
||||||
Option.when(hids.nonEmpty && province.food > 0) {
|
Option.when(hids.nonEmpty && province.food > 0) {
|
||||||
AlmsAvailableCommand(
|
AlmsAvailableCommand(
|
||||||
availableHeroIds = hids,
|
availableHeroIds = hids,
|
||||||
actingProvinceId = provinceId,
|
actingProvinceId = provinceId,
|
||||||
foodAvailable =
|
foodAvailable = Math.min(MaxAlmsFood.intValue, gameState.provinces(provinceId).food)
|
||||||
Math.min(MaxAlmsFood.intValue, gameState.provinces(provinceId).food)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -1,10 +1,7 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{ApprehendOutlawAvailableCommand, ResidentOutlaw}
|
||||||
ApprehendOutlawAvailableCommand,
|
|
||||||
ResidentOutlaw
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.UNAFFILIATED_HERO_OUTLAW
|
import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.UNAFFILIATED_HERO_OUTLAW
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.hero.Hero
|
import net.eagle0.eagle.internal.hero.Hero
|
||||||
@@ -13,8 +10,7 @@ import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
|||||||
import net.eagle0.eagle.library.settings.MinVigorForApprehendOutlaw
|
import net.eagle0.eagle.library.settings.MinVigorForApprehendOutlaw
|
||||||
import net.eagle0.eagle.library.util.view_filters.HeroViewFilter
|
import net.eagle0.eagle.library.util.view_filters.HeroViewFilter
|
||||||
|
|
||||||
object AvailableApprehendOutlawCommandFactory
|
object AvailableApprehendOutlawCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
private def availableToCapture(province: Province): Vector[UnaffiliatedHero] =
|
private def availableToCapture(province: Province): Vector[UnaffiliatedHero] =
|
||||||
province.unaffiliatedHeroes
|
province.unaffiliatedHeroes
|
||||||
@@ -36,8 +32,8 @@ object AvailableApprehendOutlawCommandFactory
|
|||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[ApprehendOutlawAvailableCommand] = {
|
): Option[ApprehendOutlawAvailableCommand] = {
|
||||||
val province = gameState.provinces(provinceId)
|
val province = gameState.provinces(provinceId)
|
||||||
val outlaws = availableToCapture(province)
|
val outlaws = availableToCapture(province)
|
||||||
val actors = availableActors(gameState, province)
|
val actors = availableActors(gameState, province)
|
||||||
|
|
||||||
Option.when(outlaws.nonEmpty && actors.nonEmpty) {
|
Option.when(outlaws.nonEmpty && actors.nonEmpty) {
|
||||||
ApprehendOutlawAvailableCommand(
|
ApprehendOutlawAvailableCommand(
|
||||||
|
|||||||
+3
-8
@@ -1,16 +1,12 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{ArmTroopsAvailableCommand, ArmamentCost}
|
||||||
ArmTroopsAvailableCommand,
|
|
||||||
ArmamentCost
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
import net.eagle0.eagle.library.util.LegacyBattalionUtils
|
import net.eagle0.eagle.library.util.LegacyBattalionUtils
|
||||||
|
|
||||||
object AvailableArmTroopsCommandFactory
|
object AvailableArmTroopsCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
@@ -59,8 +55,7 @@ object AvailableArmTroopsCommandFactory
|
|||||||
actingProvinceId = provinceId,
|
actingProvinceId = provinceId,
|
||||||
availableBattalions = affordableBattalions.map(_.id),
|
availableBattalions = affordableBattalions.map(_.id),
|
||||||
armamentCosts = armamentCosts,
|
armamentCosts = armamentCosts,
|
||||||
maxArmament =
|
maxArmament = LegacyProvinceUtils.effectiveInfrastructure(province).floor.toInt,
|
||||||
LegacyProvinceUtils.effectiveInfrastructure(province).floor.toInt,
|
|
||||||
availableBattalionTypeIds = gameState.battalionTypes.map(_.typeId)
|
availableBattalionTypeIds = gameState.battalionTypes.map(_.typeId)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+16
-21
@@ -19,8 +19,7 @@ import net.eagle0.eagle.library.util.{IncomingArmyUtils, ProvinceDistances}
|
|||||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||||
|
|
||||||
object AvailableAttackDecisionCommandFactory
|
object AvailableAttackDecisionCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
// We need to include the armies that have already withdrawn or advanced, in a consistent sort order,
|
// We need to include the armies that have already withdrawn or advanced, in a consistent sort order,
|
||||||
// so that the player doesn't learn what others have done by the shifts.
|
// so that the player doesn't learn what others have done by the shifts.
|
||||||
@@ -72,8 +71,7 @@ object AvailableAttackDecisionCommandFactory
|
|||||||
factionId = defenderFid,
|
factionId = defenderFid,
|
||||||
heroCount = heroCount,
|
heroCount = heroCount,
|
||||||
troopCount = troopCount,
|
troopCount = troopCount,
|
||||||
hostility =
|
hostility = LegacyFactionUtils.hostilityStatus(defenderFid, attackerFid, gs),
|
||||||
LegacyFactionUtils.hostilityStatus(defenderFid, attackerFid, gs),
|
|
||||||
originProvinceId = pid
|
originProvinceId = pid
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -219,24 +217,21 @@ object AvailableAttackDecisionCommandFactory
|
|||||||
s"Incoming armies in attack decision phase are not mutually allied in province $provinceId"
|
s"Incoming armies in attack decision phase are not mutually allied in province $provinceId"
|
||||||
)
|
)
|
||||||
|
|
||||||
myDecidingHostileArmies(provinceId, factionId, gameState).flatMap {
|
myDecidingHostileArmies(provinceId, factionId, gameState).flatMap { hostileArmyGroup =>
|
||||||
hostileArmyGroup =>
|
decisionOptions(gameState, provinceId, factionId) match {
|
||||||
decisionOptions(gameState, provinceId, factionId) match {
|
case items if items.isEmpty => None
|
||||||
case items if items.isEmpty => None
|
case nonemptyDecisionOptions =>
|
||||||
case nonemptyDecisionOptions =>
|
Some(
|
||||||
Some(
|
AttackDecisionAvailableCommand(
|
||||||
AttackDecisionAvailableCommand(
|
actingProvinceId = provinceId,
|
||||||
actingProvinceId = provinceId,
|
armies = armyStats(factionId, provinceId, gameState),
|
||||||
armies = armyStats(factionId, provinceId, gameState),
|
actingUnits = hostileArmyGroup.armies
|
||||||
actingUnits = hostileArmyGroup.armies
|
.flatMap(_.getArmy.units)
|
||||||
.flatMap(_.getArmy.units)
|
.map(cu => ExpandedCombatUnitUtils.expandedCombatUnit(cu, gameState)),
|
||||||
.map(cu =>
|
availableDecisions = nonemptyDecisionOptions
|
||||||
ExpandedCombatUnitUtils.expandedCombatUnit(cu, gameState)
|
|
||||||
),
|
|
||||||
availableDecisions = nonemptyDecisionOptions
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+97
-114
@@ -38,7 +38,7 @@ object AvailableCommandsFactory {
|
|||||||
case (_: OrganizeTroopsSelectedCommand, _) => false
|
case (_: OrganizeTroopsSelectedCommand, _) => false
|
||||||
case (_: ExileVassalSelectedCommand, _) => false
|
case (_: ExileVassalSelectedCommand, _) => false
|
||||||
case (_: SwearBrotherhoodSelectedCommand, _) => false
|
case (_: SwearBrotherhoodSelectedCommand, _) => false
|
||||||
case (last, next) => AvailableCommandTypeMap.matches(next, last)
|
case (last, next) => AvailableCommandTypeMap.matches(next, last)
|
||||||
}
|
}
|
||||||
|
|
||||||
def suggestedProvinceId(
|
def suggestedProvinceId(
|
||||||
@@ -74,22 +74,20 @@ object AvailableCommandsFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AvailableCommandsFactory(
|
class AvailableCommandsFactory(
|
||||||
private val travelingFactories: Vector[AvailableCommandsFactoryForType] =
|
private val travelingFactories: Vector[AvailableCommandsFactoryForType] = Vector(
|
||||||
Vector(
|
AvailableRecruitHeroesCommandFactory,
|
||||||
AvailableRecruitHeroesCommandFactory,
|
AvailableDeclineQuestCommandsFactory,
|
||||||
AvailableDeclineQuestCommandsFactory,
|
AvailableDivineCommandsFactory,
|
||||||
AvailableDivineCommandsFactory,
|
AvailableArmTroopsCommandFactory,
|
||||||
AvailableArmTroopsCommandFactory,
|
AvailableTradeCommandFactory,
|
||||||
AvailableTradeCommandFactory,
|
AvailableManagePrisonersCommandFactory,
|
||||||
AvailableManagePrisonersCommandFactory,
|
AvailableReturnCommandsFactory
|
||||||
AvailableReturnCommandsFactory
|
),
|
||||||
),
|
private val handleRiotFactories: Vector[AvailableCommandsFactoryForType] = Vector(
|
||||||
private val handleRiotFactories: Vector[AvailableCommandsFactoryForType] =
|
AvailableHandleRiotCrackDownCommandFactory,
|
||||||
Vector(
|
AvailableHandleRiotDoNothingCommandFactory,
|
||||||
AvailableHandleRiotCrackDownCommandFactory,
|
AvailableHandleRiotGiveCommandFactory
|
||||||
AvailableHandleRiotDoNothingCommandFactory,
|
),
|
||||||
AvailableHandleRiotGiveCommandFactory
|
|
||||||
),
|
|
||||||
private val freeForAllDecisionPhaseFactories: Vector[
|
private val freeForAllDecisionPhaseFactories: Vector[
|
||||||
AvailableCommandsFactoryForType
|
AvailableCommandsFactoryForType
|
||||||
] = Vector(
|
] = Vector(
|
||||||
@@ -100,33 +98,31 @@ class AvailableCommandsFactory(
|
|||||||
] = Vector(
|
] = Vector(
|
||||||
AvailableAttackDecisionCommandFactory
|
AvailableAttackDecisionCommandFactory
|
||||||
),
|
),
|
||||||
private val defensePhaseFactories: Vector[AvailableCommandsFactoryForType] =
|
private val defensePhaseFactories: Vector[AvailableCommandsFactoryForType] = Vector(
|
||||||
Vector(
|
AvailableResolveTributeCommandsFactory,
|
||||||
AvailableResolveTributeCommandsFactory,
|
AvailableDefendCommandsFactory
|
||||||
AvailableDefendCommandsFactory
|
),
|
||||||
),
|
private val commandPhaseFactories: Vector[AvailableCommandsFactoryForType] = Vector(
|
||||||
private val commandPhaseFactories: Vector[AvailableCommandsFactoryForType] =
|
AvailableApprehendOutlawCommandFactory,
|
||||||
Vector(
|
AvailableControlWeatherCommandsFactory,
|
||||||
AvailableApprehendOutlawCommandFactory,
|
AvailableDiplomacyCommandsFactory,
|
||||||
AvailableControlWeatherCommandsFactory,
|
AvailableExileVassalCommandFactory,
|
||||||
AvailableDiplomacyCommandsFactory,
|
AvailableFeastCommandFactory,
|
||||||
AvailableExileVassalCommandFactory,
|
AvailableHeroGiftCommandFactory,
|
||||||
AvailableFeastCommandFactory,
|
AvailableImproveCommandsFactory,
|
||||||
AvailableHeroGiftCommandFactory,
|
AvailableMarchCommandFactory,
|
||||||
AvailableImproveCommandsFactory,
|
AvailableIssueOrdersCommandFactory,
|
||||||
AvailableMarchCommandFactory,
|
AvailableOrganizeTroopsCommandsFactory,
|
||||||
AvailableIssueOrdersCommandFactory,
|
AvailableAlmsCommandFactory,
|
||||||
AvailableOrganizeTroopsCommandsFactory,
|
AvailableReconCommandFactory,
|
||||||
AvailableAlmsCommandFactory,
|
AvailableRestCommandsFactory,
|
||||||
AvailableReconCommandFactory,
|
AvailableSendSuppliesCommandFactory,
|
||||||
AvailableRestCommandsFactory,
|
AvailableStartEpidemicCommandFactory,
|
||||||
AvailableSendSuppliesCommandFactory,
|
AvailableSuppressBeastsCommandFactory,
|
||||||
AvailableStartEpidemicCommandFactory,
|
AvailableSwearBrotherhoodCommandFactory,
|
||||||
AvailableSuppressBeastsCommandFactory,
|
AvailableTrainCommandsFactory,
|
||||||
AvailableSwearBrotherhoodCommandFactory,
|
AvailableTravelCommandsFactory
|
||||||
AvailableTrainCommandsFactory,
|
)
|
||||||
AvailableTravelCommandsFactory
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
private def allCommands(
|
private def allCommands(
|
||||||
factories: Vector[AvailableCommandsFactoryForType],
|
factories: Vector[AvailableCommandsFactoryForType],
|
||||||
@@ -181,13 +177,13 @@ class AvailableCommandsFactory(
|
|||||||
pid: ProvinceId,
|
pid: ProvinceId,
|
||||||
commands: Vector[AvailableCommand]
|
commands: Vector[AvailableCommand]
|
||||||
): Int =
|
): Int =
|
||||||
commands.zipWithIndex
|
commands.zipWithIndex.find {
|
||||||
.find { case (ac, _) =>
|
case (ac, _) =>
|
||||||
gs.provinces
|
gs.provinces
|
||||||
.get(pid)
|
.get(pid)
|
||||||
.map(_.lastCommand)
|
.map(_.lastCommand)
|
||||||
.forall(last => AvailableCommandsFactory.shouldFollow(last, ac))
|
.forall(last => AvailableCommandsFactory.shouldFollow(last, ac))
|
||||||
}
|
}
|
||||||
.map(_._2)
|
.map(_._2)
|
||||||
.getOrElse(0)
|
.getOrElse(0)
|
||||||
|
|
||||||
@@ -220,8 +216,7 @@ class AvailableCommandsFactory(
|
|||||||
gs = gs,
|
gs = gs,
|
||||||
pid = pid,
|
pid = pid,
|
||||||
commands =
|
commands =
|
||||||
if gs.provinces(pid).rulerIsTraveling then
|
if gs.provinces(pid).rulerIsTraveling then allCommands(travelingFactories, gs, fid(gs, pid).get, pid)
|
||||||
allCommands(travelingFactories, gs, fid(gs, pid).get, pid)
|
|
||||||
else
|
else
|
||||||
allCommands(
|
allCommands(
|
||||||
commandPhaseFactories,
|
commandPhaseFactories,
|
||||||
@@ -235,8 +230,7 @@ class AvailableCommandsFactory(
|
|||||||
gs: GameState,
|
gs: GameState,
|
||||||
pid: ProvinceId
|
pid: ProvinceId
|
||||||
): Boolean =
|
): Boolean =
|
||||||
if gs.provinces(pid).rulerIsTraveling then
|
if gs.provinces(pid).rulerIsTraveling then hasCommand(travelingFactories, gs, fid(gs, pid).get, pid)
|
||||||
hasCommand(travelingFactories, gs, fid(gs, pid).get, pid)
|
|
||||||
else
|
else
|
||||||
hasCommand(
|
hasCommand(
|
||||||
commandPhaseFactories,
|
commandPhaseFactories,
|
||||||
@@ -440,27 +434,19 @@ class AvailableCommandsFactory(
|
|||||||
gs: GameState,
|
gs: GameState,
|
||||||
fid: FactionId
|
fid: FactionId
|
||||||
): Boolean =
|
): Boolean =
|
||||||
factionLeaderProvinces(gs, fid).exists(p =>
|
factionLeaderProvinces(gs, fid).exists(p => defensePhaseCommandsForProvince(gs, p.id).isDefined)
|
||||||
defensePhaseCommandsForProvince(gs, p.id).isDefined
|
|
||||||
)
|
|
||||||
|
|
||||||
def hasAvailableHandleRiotPhaseCommands(gs: GameState): Boolean =
|
def hasAvailableHandleRiotPhaseCommands(gs: GameState): Boolean =
|
||||||
gs.factions.keys.exists(fid =>
|
gs.factions.keys.exists(fid => handleRiotPhaseCommandsForFactionLeader(gs, fid).nonEmpty)
|
||||||
handleRiotPhaseCommandsForFactionLeader(gs, fid).nonEmpty
|
|
||||||
)
|
|
||||||
|
|
||||||
def hasAvailablePlayerCommandsPhaseCommands(gs: GameState): Boolean =
|
def hasAvailablePlayerCommandsPhaseCommands(gs: GameState): Boolean =
|
||||||
gs.factions.keys.exists(fid => hasCommandsPhaseCommandsForPlayer(gs, fid))
|
gs.factions.keys.exists(fid => hasCommandsPhaseCommandsForPlayer(gs, fid))
|
||||||
|
|
||||||
def hasAvailableFreeForAllDecisionPhaseCommands(gs: GameState): Boolean =
|
def hasAvailableFreeForAllDecisionPhaseCommands(gs: GameState): Boolean =
|
||||||
gs.factions.keys.exists(fid =>
|
gs.factions.keys.exists(fid => freeForAllDecisionPhasePlayerCommands(gs, fid).nonEmpty)
|
||||||
freeForAllDecisionPhasePlayerCommands(gs, fid).nonEmpty
|
|
||||||
)
|
|
||||||
|
|
||||||
def hasAvailableAttackDecisionPhaseCommands(gs: GameState): Boolean =
|
def hasAvailableAttackDecisionPhaseCommands(gs: GameState): Boolean =
|
||||||
gs.factions.keys.exists(fid =>
|
gs.factions.keys.exists(fid => attackDecisionPhasePlayerCommands(gs, fid).nonEmpty)
|
||||||
attackDecisionPhasePlayerCommands(gs, fid).nonEmpty
|
|
||||||
)
|
|
||||||
|
|
||||||
def hasAvailablePlayerDefenseCommands(gs: GameState): Boolean =
|
def hasAvailablePlayerDefenseCommands(gs: GameState): Boolean =
|
||||||
gs.factions.keys.exists(fid => hasPlayerDefenseCommandsForPlayer(gs, fid))
|
gs.factions.keys.exists(fid => hasPlayerDefenseCommandsForPlayer(gs, fid))
|
||||||
@@ -471,30 +457,30 @@ class AvailableCommandsFactory(
|
|||||||
): SortedMap[ProvinceId, OneProvinceAvailableCommands] =
|
): SortedMap[ProvinceId, OneProvinceAvailableCommands] =
|
||||||
(
|
(
|
||||||
for {
|
for {
|
||||||
faction <- gs.factions.get(fid)
|
faction <- gs.factions.get(fid)
|
||||||
diplomacyCommand <- AvailableResolveInvitationCommandFactory
|
diplomacyCommand <- AvailableResolveInvitationCommandFactory
|
||||||
.availableCommand(gs, faction.id)
|
.availableCommand(gs, faction.id)
|
||||||
.orElse(
|
.orElse(
|
||||||
AvailableResolveTruceOfferCommandFactory
|
AvailableResolveTruceOfferCommandFactory
|
||||||
.availableCommand(gs, faction.id)
|
.availableCommand(gs, faction.id)
|
||||||
)
|
)
|
||||||
.orElse(
|
.orElse(
|
||||||
AvailableResolveRansomOfferCommandFactory
|
AvailableResolveRansomOfferCommandFactory
|
||||||
.availableCommand(gs, faction.id)
|
.availableCommand(gs, faction.id)
|
||||||
)
|
)
|
||||||
.orElse(
|
.orElse(
|
||||||
AvailableResolveAllianceOfferCommandFactory
|
AvailableResolveAllianceOfferCommandFactory
|
||||||
.availableCommand(gs, faction.id)
|
.availableCommand(gs, faction.id)
|
||||||
)
|
)
|
||||||
.orElse(
|
.orElse(
|
||||||
AvailableResolveBreakAllianceCommandFactory
|
AvailableResolveBreakAllianceCommandFactory
|
||||||
.availableCommand(gs, faction.id)
|
.availableCommand(gs, faction.id)
|
||||||
)
|
)
|
||||||
oneProvinceAvailableCommand <- optionalOneProvinceAvailableCommands(
|
oneProvinceAvailableCommand <- optionalOneProvinceAvailableCommands(
|
||||||
gs = gs,
|
gs = gs,
|
||||||
0,
|
0,
|
||||||
Vector(diplomacyCommand)
|
Vector(diplomacyCommand)
|
||||||
)
|
)
|
||||||
} yield SortedMap(0 -> oneProvinceAvailableCommand)
|
} yield SortedMap(0 -> oneProvinceAvailableCommand)
|
||||||
).getOrElse(SortedMap.empty)
|
).getOrElse(SortedMap.empty)
|
||||||
|
|
||||||
@@ -504,14 +490,14 @@ class AvailableCommandsFactory(
|
|||||||
): SortedMap[ProvinceId, OneProvinceAvailableCommands] =
|
): SortedMap[ProvinceId, OneProvinceAvailableCommands] =
|
||||||
(
|
(
|
||||||
for {
|
for {
|
||||||
faction <- gs.factions.get(fid)
|
faction <- gs.factions.get(fid)
|
||||||
pleaseRecruitMeCommand <- AvailablePleaseRecruitMeCommandFactory
|
pleaseRecruitMeCommand <- AvailablePleaseRecruitMeCommandFactory
|
||||||
.availableCommand(gs, faction.id)
|
.availableCommand(gs, faction.id)
|
||||||
oneProvinceAvailableCommand <- optionalOneProvinceAvailableCommands(
|
oneProvinceAvailableCommand <- optionalOneProvinceAvailableCommands(
|
||||||
gs = gs,
|
gs = gs,
|
||||||
0,
|
0,
|
||||||
Vector(pleaseRecruitMeCommand)
|
Vector(pleaseRecruitMeCommand)
|
||||||
)
|
)
|
||||||
} yield SortedMap(0 -> oneProvinceAvailableCommand)
|
} yield SortedMap(0 -> oneProvinceAvailableCommand)
|
||||||
).getOrElse(SortedMap.empty)
|
).getOrElse(SortedMap.empty)
|
||||||
|
|
||||||
@@ -527,9 +513,7 @@ class AvailableCommandsFactory(
|
|||||||
LegacyFactionUtils
|
LegacyFactionUtils
|
||||||
.provinces(fid, gs)
|
.provinces(fid, gs)
|
||||||
.map(_.id)
|
.map(_.id)
|
||||||
.flatMap(pid =>
|
.flatMap(pid => availableAftermathCommandsForProvince(gs, pid).map(pid -> _))*
|
||||||
availableAftermathCommandsForProvince(gs, pid).map(pid -> _)
|
|
||||||
)*
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def availablePlayerCommands(
|
def availablePlayerCommands(
|
||||||
@@ -537,42 +521,41 @@ class AvailableCommandsFactory(
|
|||||||
fid: FactionId
|
fid: FactionId
|
||||||
): SortedMap[ProvinceId, OneProvinceAvailableCommands] =
|
): SortedMap[ProvinceId, OneProvinceAvailableCommands] =
|
||||||
gs.currentPhase match {
|
gs.currentPhase match {
|
||||||
case HANDLE_RIOT => handleRiotPhaseCommandsForFactionLeader(gs, fid)
|
case HANDLE_RIOT => handleRiotPhaseCommandsForFactionLeader(gs, fid)
|
||||||
case PLEASE_RECRUIT_ME => pleaseRecruitMePhasePlayerCommands(gs, fid)
|
case PLEASE_RECRUIT_ME => pleaseRecruitMePhasePlayerCommands(gs, fid)
|
||||||
case PLAYER_COMMANDS => commandsPhasePlayerCommands(gs, fid)
|
case PLAYER_COMMANDS => commandsPhasePlayerCommands(gs, fid)
|
||||||
case FREE_FOR_ALL_DECISION =>
|
case FREE_FOR_ALL_DECISION =>
|
||||||
freeForAllDecisionPhasePlayerCommands(gs, fid)
|
freeForAllDecisionPhasePlayerCommands(gs, fid)
|
||||||
case ATTACK_DECISION => attackDecisionPhasePlayerCommands(gs, fid)
|
case ATTACK_DECISION => attackDecisionPhasePlayerCommands(gs, fid)
|
||||||
case DEFENSE_DECISION => defensePhasePlayerCommands(gs, fid)
|
case DEFENSE_DECISION => defensePhasePlayerCommands(gs, fid)
|
||||||
case BATTLE_AFTERMATH => availableAftermathPhasePlayerCommands(gs, fid)
|
case BATTLE_AFTERMATH => availableAftermathPhasePlayerCommands(gs, fid)
|
||||||
case DIPLOMACY_RESOLUTION =>
|
case DIPLOMACY_RESOLUTION =>
|
||||||
diplomacyResolutionPhasePlayerCommands(gs, fid)
|
diplomacyResolutionPhasePlayerCommands(gs, fid)
|
||||||
case _ => SortedMap.empty[ProvinceId, OneProvinceAvailableCommands]
|
case _ => SortedMap.empty[ProvinceId, OneProvinceAvailableCommands]
|
||||||
}
|
}
|
||||||
|
|
||||||
def hasAvailablePlayerCommands(gs: GameState): Boolean = {
|
def hasAvailablePlayerCommands(gs: GameState): Boolean =
|
||||||
gs.currentPhase match {
|
gs.currentPhase match {
|
||||||
case DIPLOMACY_RESOLUTION =>
|
case DIPLOMACY_RESOLUTION =>
|
||||||
hasAvailableDiplomacyResolutionPhaseCommands(gs)
|
hasAvailableDiplomacyResolutionPhaseCommands(gs)
|
||||||
case HANDLE_RIOT =>
|
case HANDLE_RIOT =>
|
||||||
hasAvailableHandleRiotPhaseCommands(gs)
|
hasAvailableHandleRiotPhaseCommands(gs)
|
||||||
case PLEASE_RECRUIT_ME =>
|
case PLEASE_RECRUIT_ME =>
|
||||||
hasAvailablePleaseRecruitMePhaseCommands(gs)
|
hasAvailablePleaseRecruitMePhaseCommands(gs)
|
||||||
case DEFENSE_DECISION =>
|
case DEFENSE_DECISION =>
|
||||||
hasAvailablePlayerDefenseCommands(gs)
|
hasAvailablePlayerDefenseCommands(gs)
|
||||||
case ATTACK_DECISION =>
|
case ATTACK_DECISION =>
|
||||||
hasAvailableAttackDecisionPhaseCommands(gs)
|
hasAvailableAttackDecisionPhaseCommands(gs)
|
||||||
case FREE_FOR_ALL_DECISION =>
|
case FREE_FOR_ALL_DECISION =>
|
||||||
hasAvailableFreeForAllDecisionPhaseCommands(gs)
|
hasAvailableFreeForAllDecisionPhaseCommands(gs)
|
||||||
case PLAYER_COMMANDS =>
|
case PLAYER_COMMANDS =>
|
||||||
gs.factions.keys.exists { fid =>
|
gs.factions.keys.exists { fid =>
|
||||||
hasCommandsPhaseCommandsForPlayer(gs, fid)
|
hasCommandsPhaseCommandsForPlayer(gs, fid)
|
||||||
}
|
}
|
||||||
case BATTLE_AFTERMATH =>
|
case BATTLE_AFTERMATH =>
|
||||||
gs.factions.keys.exists { fid =>
|
gs.factions.keys.exists { fid =>
|
||||||
availableAftermathPhasePlayerCommands(gs, fid).nonEmpty
|
availableAftermathPhasePlayerCommands(gs, fid).nonEmpty
|
||||||
}
|
}
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -14,8 +14,7 @@ import net.eagle0.eagle.internal.province.Province
|
|||||||
import net.eagle0.eagle.library.settings.MinVigorForControlWeather
|
import net.eagle0.eagle.library.settings.MinVigorForControlWeather
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableControlWeatherCommandsFactory
|
object AvailableControlWeatherCommandsFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
private def makeControlWeatherCommand(
|
private def makeControlWeatherCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
@@ -34,12 +33,10 @@ object AvailableControlWeatherCommandsFactory
|
|||||||
TargetProvinceOptions(
|
TargetProvinceOptions(
|
||||||
provinceId = pid,
|
provinceId = pid,
|
||||||
controlWeatherTypes = (
|
controlWeatherTypes = (
|
||||||
if LegacyProvinceUtils.hasBlizzard(gameState.provinces(pid)) then
|
if LegacyProvinceUtils.hasBlizzard(gameState.provinces(pid)) then Vector(CONTROL_WEATHER_END_BLIZZARD)
|
||||||
Vector(CONTROL_WEATHER_END_BLIZZARD)
|
|
||||||
else Vector(CONTROL_WEATHER_START_BLIZZARD)
|
else Vector(CONTROL_WEATHER_START_BLIZZARD)
|
||||||
) ++ (
|
) ++ (
|
||||||
if LegacyProvinceUtils.hasDrought(gameState.provinces(pid)) then
|
if LegacyProvinceUtils.hasDrought(gameState.provinces(pid)) then Vector(CONTROL_WEATHER_END_DROUGHT)
|
||||||
Vector(CONTROL_WEATHER_END_DROUGHT)
|
|
||||||
else Vector(CONTROL_WEATHER_START_DROUGHT)
|
else Vector(CONTROL_WEATHER_START_DROUGHT)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+2
-5
@@ -8,8 +8,7 @@ import net.eagle0.eagle.internal.game_state.GameState
|
|||||||
import net.eagle0.eagle.library.actions.availability.ExpandedUnaffiliatedHeroUtils.expandedUnaffiliatedHero
|
import net.eagle0.eagle.library.actions.availability.ExpandedUnaffiliatedHeroUtils.expandedUnaffiliatedHero
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableDeclineQuestCommandsFactory
|
object AvailableDeclineQuestCommandsFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
@@ -27,9 +26,7 @@ object AvailableDeclineQuestCommandsFactory
|
|||||||
Option.when(withQuests.nonEmpty) {
|
Option.when(withQuests.nonEmpty) {
|
||||||
DeclineQuestAvailableCommand(
|
DeclineQuestAvailableCommand(
|
||||||
actingProvinceId = provinceId,
|
actingProvinceId = provinceId,
|
||||||
declinableHeroes = withQuests.map(uh =>
|
declinableHeroes = withQuests.map(uh => expandedUnaffiliatedHero(gs = gameState, uh = uh))
|
||||||
expandedUnaffiliatedHero(gs = gameState, uh = uh)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-21
@@ -6,14 +6,8 @@ import net.eagle0.eagle.api.command.util.battalion_with_food_cost.BattalionWithF
|
|||||||
import net.eagle0.eagle.internal.army.Attacking
|
import net.eagle0.eagle.internal.army.Attacking
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.province.Province
|
import net.eagle0.eagle.internal.province.Province
|
||||||
import net.eagle0.eagle.library.settings.{
|
import net.eagle0.eagle.library.settings.{MaxCombatUnitCountPerSide, MinVigorForDefend}
|
||||||
MaxCombatUnitCountPerSide,
|
import net.eagle0.eagle.library.util.{BattalionSuitability, LegacyBattalionUtils}
|
||||||
MinVigorForDefend
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.{
|
|
||||||
BattalionSuitability,
|
|
||||||
LegacyBattalionUtils
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.CombatUnitSelector
|
import net.eagle0.eagle.library.util.command_choice_helpers.CombatUnitSelector
|
||||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||||
|
|
||||||
@@ -52,7 +46,7 @@ object AvailableDefendCommandsFactory extends AvailableCommandsFactoryForType {
|
|||||||
army.status match {
|
army.status match {
|
||||||
case Attacking(_) =>
|
case Attacking(_) =>
|
||||||
true
|
true
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +58,7 @@ object AvailableDefendCommandsFactory extends AvailableCommandsFactoryForType {
|
|||||||
.filter(_.vigor >= MinVigorForDefend.doubleValue)
|
.filter(_.vigor >= MinVigorForDefend.doubleValue)
|
||||||
if availableHeroes.isEmpty then return None
|
if availableHeroes.isEmpty then return None
|
||||||
|
|
||||||
val availableBattalions = province.battalionIds.map(gameState.battalions)
|
val availableBattalions = province.battalionIds.map(gameState.battalions)
|
||||||
val availableBattalionsWithCosts = availableBattalions.map(batt =>
|
val availableBattalionsWithCosts = availableBattalions.map(batt =>
|
||||||
BattalionWithFoodCost(
|
BattalionWithFoodCost(
|
||||||
battalionId = batt.id,
|
battalionId = batt.id,
|
||||||
@@ -76,13 +70,12 @@ object AvailableDefendCommandsFactory extends AvailableCommandsFactoryForType {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val recommendedUnits = CombatUnitSelector.selectedCombatBattalions(
|
val recommendedUnits = CombatUnitSelector.selectedCombatBattalions(
|
||||||
desiredCount =
|
desiredCount = availableHeroes.size.min(MaxCombatUnitCountPerSide.intValue),
|
||||||
availableHeroes.size.min(MaxCombatUnitCountPerSide.intValue),
|
|
||||||
heroes = availableHeroes.toVector,
|
heroes = availableHeroes.toVector,
|
||||||
battalions = availableBattalions.toVector,
|
battalions = availableBattalions.toVector,
|
||||||
battalionTypes = gameState.battalionTypes.toVector
|
battalionTypes = gameState.battalionTypes.toVector
|
||||||
)
|
)
|
||||||
val hostileUnits = hostileArmies
|
val hostileUnits = hostileArmies
|
||||||
.flatMap(_.armies)
|
.flatMap(_.armies)
|
||||||
.flatMap(_.army)
|
.flatMap(_.army)
|
||||||
.flatMap(_.units)
|
.flatMap(_.units)
|
||||||
@@ -93,15 +86,13 @@ object AvailableDefendCommandsFactory extends AvailableCommandsFactoryForType {
|
|||||||
.toVector
|
.toVector
|
||||||
.map(_.id),
|
.map(_.id),
|
||||||
actingProvinceId = province.id,
|
actingProvinceId = province.id,
|
||||||
suitableBattalionsForHeroes =
|
suitableBattalionsForHeroes = BattalionSuitability.suitableBattalionsForHeroes(
|
||||||
BattalionSuitability.suitableBattalionsForHeroes(
|
hs = availableHeroes.toVector,
|
||||||
hs = availableHeroes.toVector,
|
bs = availableBattalions.toVector,
|
||||||
bs = availableBattalions.toVector,
|
bts = gameState.battalionTypes.toVector
|
||||||
bts = gameState.battalionTypes.toVector
|
),
|
||||||
),
|
|
||||||
availableBattalions = availableBattalionsWithCosts,
|
availableBattalions = availableBattalionsWithCosts,
|
||||||
availableFleeProvinceIds =
|
availableFleeProvinceIds = sortedFleeProvinces(from = province, gs = gameState),
|
||||||
sortedFleeProvinces(from = province, gs = gameState),
|
|
||||||
recommendedUnits = recommendedUnits,
|
recommendedUnits = recommendedUnits,
|
||||||
hostileFactionIds = hostileArmies.toVector.map(_.factionId).distinct,
|
hostileFactionIds = hostileArmies.toVector.map(_.factionId).distinct,
|
||||||
hostileHeroCount = hostileUnits.size,
|
hostileHeroCount = hostileUnits.size,
|
||||||
|
|||||||
+52
-61
@@ -31,8 +31,7 @@ import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
|||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
import net.eagle0.eagle.library.util.ProvinceDistances
|
import net.eagle0.eagle.library.util.ProvinceDistances
|
||||||
|
|
||||||
object AvailableDiplomacyCommandsFactory
|
object AvailableDiplomacyCommandsFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
def availableHeroIds(gs: GameState, pid: ProvinceId): Vector[HeroId] =
|
def availableHeroIds(gs: GameState, pid: ProvinceId): Vector[HeroId] =
|
||||||
gs.provinces(pid)
|
gs.provinces(pid)
|
||||||
@@ -42,9 +41,7 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
|
|
||||||
def recommendedHeroId(gs: GameState, availableHids: Seq[HeroId]): HeroId =
|
def recommendedHeroId(gs: GameState, availableHids: Seq[HeroId]): HeroId =
|
||||||
availableHids
|
availableHids
|
||||||
.sortBy(hid =>
|
.sortBy(hid => (LegacyHeroUtils.fatigue(gs.heroes(hid)), -gs.heroes(hid).vigor)) match {
|
||||||
(LegacyHeroUtils.fatigue(gs.heroes(hid)), -gs.heroes(hid).vigor)
|
|
||||||
) match {
|
|
||||||
case sortedHeroes =>
|
case sortedHeroes =>
|
||||||
sortedHeroes
|
sortedHeroes
|
||||||
.find(hid => !LegacyFactionUtils.isFactionHead(hid, gs))
|
.find(hid => !LegacyFactionUtils.isFactionHead(hid, gs))
|
||||||
@@ -123,38 +120,36 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
pid: ProvinceId,
|
pid: ProvinceId,
|
||||||
actingFid: FactionId,
|
actingFid: FactionId,
|
||||||
targetFid: FactionId
|
targetFid: FactionId
|
||||||
): Boolean = {
|
): Boolean =
|
||||||
LegacyFactionUtils.provinces(targetFid, gs).headOption.exists {
|
LegacyFactionUtils.provinces(targetFid, gs).headOption.exists { targetProvince =>
|
||||||
targetProvince =>
|
val eligibleNeighbors: ProvinceId => Vector[ProvinceId] =
|
||||||
val eligibleNeighbors: ProvinceId => Vector[ProvinceId] =
|
p =>
|
||||||
p =>
|
gs.provinces(p)
|
||||||
gs.provinces(p)
|
.neighbors
|
||||||
.neighbors
|
.map(_.provinceId)
|
||||||
.map(_.provinceId)
|
.map(gs.provinces)
|
||||||
.map(gs.provinces)
|
.filter(neighborProvince =>
|
||||||
.filter(neighborProvince =>
|
// inviting across empty province
|
||||||
// inviting across empty province
|
neighborProvince.rulingFactionId.isEmpty ||
|
||||||
neighborProvince.rulingFactionId.isEmpty ||
|
// inviting across your own province
|
||||||
// inviting across your own province
|
neighborProvince.rulingFactionId.contains(actingFid) ||
|
||||||
neighborProvince.rulingFactionId.contains(actingFid) ||
|
// inviting across the target's province
|
||||||
// inviting across the target's province
|
neighborProvince.rulingFactionId.contains(targetFid) ||
|
||||||
neighborProvince.rulingFactionId.contains(targetFid) ||
|
// inviting across your ally's province
|
||||||
// inviting across your ally's province
|
LegacyFactionUtils
|
||||||
LegacyFactionUtils
|
.hasAlliance(
|
||||||
.hasAlliance(
|
neighborProvince.rulingFactionId.get,
|
||||||
neighborProvince.rulingFactionId.get,
|
actingFid,
|
||||||
actingFid,
|
gs
|
||||||
gs
|
)
|
||||||
)
|
)
|
||||||
)
|
.map(_.id)
|
||||||
.map(_.id)
|
.toVector
|
||||||
.toVector
|
|
||||||
|
|
||||||
ProvinceDistances
|
ProvinceDistances
|
||||||
.distance(pid, targetProvince.id, eligibleNeighbors)
|
.distance(pid, targetProvince.id, eligibleNeighbors)
|
||||||
.isDefined
|
.isDefined
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private def inviteOptionForTargetFaction(
|
private def inviteOptionForTargetFaction(
|
||||||
gs: GameState,
|
gs: GameState,
|
||||||
@@ -191,8 +186,8 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
targetFid: FactionId
|
targetFid: FactionId
|
||||||
): Vector[DiplomacyOption] = {
|
): Vector[DiplomacyOption] = {
|
||||||
val prisonersAvailableToOffer = availablePrisoners(gs.provinces(pid))
|
val prisonersAvailableToOffer = availablePrisoners(gs.provinces(pid))
|
||||||
val hostagesAvailableToOffer = availableHostages(gs, pid)
|
val hostagesAvailableToOffer = availableHostages(gs, pid)
|
||||||
val goldAvailableToOffer = gs.provinces(pid).gold
|
val goldAvailableToOffer = gs.provinces(pid).gold
|
||||||
|
|
||||||
if prisonersAvailableToOffer.nonEmpty || hostagesAvailableToOffer.nonEmpty || goldAvailableToOffer > 0
|
if prisonersAvailableToOffer.nonEmpty || hostagesAvailableToOffer.nonEmpty || goldAvailableToOffer > 0
|
||||||
then
|
then
|
||||||
@@ -200,20 +195,19 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
gs,
|
gs,
|
||||||
factionId = actingFid,
|
factionId = actingFid,
|
||||||
targetFactionId = targetFid
|
targetFactionId = targetFid
|
||||||
)
|
).map { prisonerToBeRansomed =>
|
||||||
.map { prisonerToBeRansomed =>
|
RansomOfferOption(
|
||||||
RansomOfferOption(
|
targetFactionId = targetFid,
|
||||||
targetFactionId = targetFid,
|
ransomOffer = Some(
|
||||||
ransomOffer = Some(
|
RansomOfferDetails(
|
||||||
RansomOfferDetails(
|
prisonerToBeRansomed = Some(prisonerToBeRansomed),
|
||||||
prisonerToBeRansomed = Some(prisonerToBeRansomed),
|
prisonersOffered = prisonersAvailableToOffer,
|
||||||
prisonersOffered = prisonersAvailableToOffer,
|
hostagesOffered = hostagesAvailableToOffer,
|
||||||
hostagesOffered = hostagesAvailableToOffer,
|
goldOffered = goldAvailableToOffer
|
||||||
goldOffered = goldAvailableToOffer
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
}
|
||||||
else Vector()
|
else Vector()
|
||||||
end if
|
end if
|
||||||
}
|
}
|
||||||
@@ -264,10 +258,9 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[DiplomacyAvailableCommand] = {
|
): Option[DiplomacyAvailableCommand] =
|
||||||
if availableHeroIds(gameState, provinceId).isEmpty then None
|
if availableHeroIds(gameState, provinceId).isEmpty then None
|
||||||
else if gameState.provinces(provinceId).rulingFactionHeroIds.size < 2 then
|
else if gameState.provinces(provinceId).rulingFactionHeroIds.size < 2 then None
|
||||||
None
|
|
||||||
else {
|
else {
|
||||||
gameState.factions.keys
|
gameState.factions.keys
|
||||||
.filterNot(_ == factionId)
|
.filterNot(_ == factionId)
|
||||||
@@ -289,7 +282,7 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
.toVector
|
.toVector
|
||||||
.sortBy(opt =>
|
.sortBy(opt =>
|
||||||
LegacyFactionUtils.sortKey(gameState.factions(opt match {
|
LegacyFactionUtils.sortKey(gameState.factions(opt match {
|
||||||
case TruceOption(targetFactionId, _, _ /* unknownFieldSet */ ) =>
|
case TruceOption(targetFactionId, _, _ /* unknownFieldSet */ ) =>
|
||||||
targetFactionId
|
targetFactionId
|
||||||
case InvitationOption(
|
case InvitationOption(
|
||||||
targetFactionId,
|
targetFactionId,
|
||||||
@@ -311,7 +304,7 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
_ /* unknownFieldSet */
|
_ /* unknownFieldSet */
|
||||||
) =>
|
) =>
|
||||||
targetFactionId
|
targetFactionId
|
||||||
case DiplomacyOption.Empty => ???
|
case DiplomacyOption.Empty => ???
|
||||||
}))
|
}))
|
||||||
) match {
|
) match {
|
||||||
case items if items.isEmpty => None
|
case items if items.isEmpty => None
|
||||||
@@ -328,7 +321,6 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private def availablePrisoners(
|
private def availablePrisoners(
|
||||||
province: Province
|
province: Province
|
||||||
@@ -365,19 +357,18 @@ object AvailableDiplomacyCommandsFactory
|
|||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
targetFactionId: FactionId
|
targetFactionId: FactionId
|
||||||
): Vector[PrisonerToBeRansomed] = {
|
): Vector[PrisonerToBeRansomed] =
|
||||||
for {
|
for {
|
||||||
province <- gameState.provinces.values
|
province <- gameState.provinces.values
|
||||||
.filter(_.rulingFactionId.contains(targetFactionId))
|
.filter(_.rulingFactionId.contains(targetFactionId))
|
||||||
.toVector
|
.toVector
|
||||||
prisoner <- province.unaffiliatedHeroes.filter(
|
prisoner <- province.unaffiliatedHeroes.filter(
|
||||||
_.`type` == UNAFFILIATED_HERO_PRISONER
|
_.`type` == UNAFFILIATED_HERO_PRISONER
|
||||||
)
|
)
|
||||||
if prisoner.lastFaction.contains(factionId)
|
if prisoner.lastFaction.contains(factionId)
|
||||||
if LegacyFactionUtils.isFactionLeader(prisoner.heroId, gameState)
|
if LegacyFactionUtils.isFactionLeader(prisoner.heroId, gameState)
|
||||||
} yield PrisonerToBeRansomed(
|
} yield PrisonerToBeRansomed(
|
||||||
prisonerHeroId = prisoner.heroId,
|
prisonerHeroId = prisoner.heroId,
|
||||||
provinceIdForPrisoner = province.id
|
provinceIdForPrisoner = province.id
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -7,8 +7,7 @@ import net.eagle0.eagle.internal.province.Province
|
|||||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableExileVassalCommandFactory
|
object AvailableExileVassalCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
def exilableHeroIds(
|
def exilableHeroIds(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
|
|||||||
+20
-35
@@ -3,22 +3,13 @@ package net.eagle0.eagle.library.actions.availability
|
|||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.FreeForAllDecisionAvailableCommand
|
import net.eagle0.eagle.api.available_command.FreeForAllDecisionAvailableCommand
|
||||||
import net.eagle0.eagle.api.command.util.army_stats.ArmyStats
|
import net.eagle0.eagle.api.command.util.army_stats.ArmyStats
|
||||||
import net.eagle0.eagle.api.command.util.attack_decision_type.{
|
import net.eagle0.eagle.api.command.util.attack_decision_type.{AdvanceDecision, AttackDecisionType, WithdrawDecision}
|
||||||
AdvanceDecision,
|
|
||||||
AttackDecisionType,
|
|
||||||
WithdrawDecision
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||||
import net.eagle0.eagle.internal.army.{
|
import net.eagle0.eagle.internal.army.{AwaitingDecision, HostileArmyGroup, MovingArmy}
|
||||||
AwaitingDecision,
|
|
||||||
HostileArmyGroup,
|
|
||||||
MovingArmy
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.util.IncomingArmyUtils
|
import net.eagle0.eagle.library.util.IncomingArmyUtils
|
||||||
|
|
||||||
object AvailableFreeForAllDecisionCommandFactory
|
object AvailableFreeForAllDecisionCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
private def decisionOptions(
|
private def decisionOptions(
|
||||||
armies: Seq[MovingArmy]
|
armies: Seq[MovingArmy]
|
||||||
@@ -58,35 +49,29 @@ object AvailableFreeForAllDecisionCommandFactory
|
|||||||
pid: ProvinceId,
|
pid: ProvinceId,
|
||||||
gs: GameState
|
gs: GameState
|
||||||
): Vector[ArmyStats] =
|
): Vector[ArmyStats] =
|
||||||
relevantArmies(pid, gs).map(ia =>
|
relevantArmies(pid, gs).map(ia => IncomingArmyUtils.stats(toFid, ia.getArmy, gs, ia.originProvince))
|
||||||
IncomingArmyUtils.stats(toFid, ia.getArmy, gs, ia.originProvince)
|
|
||||||
)
|
|
||||||
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[FreeForAllDecisionAvailableCommand] = {
|
): Option[FreeForAllDecisionAvailableCommand] =
|
||||||
myHostileArmies(provinceId, factionId, gameState).flatMap {
|
myHostileArmies(provinceId, factionId, gameState).flatMap { hostileArmyGroup =>
|
||||||
hostileArmyGroup =>
|
Option.when(
|
||||||
Option.when(
|
gameState.currentPhase == RoundPhase.FREE_FOR_ALL_DECISION
|
||||||
gameState.currentPhase == RoundPhase.FREE_FOR_ALL_DECISION
|
&& !IncomingArmyUtils.incomingArmiesAreMutuallyAllied(
|
||||||
&& !IncomingArmyUtils.incomingArmiesAreMutuallyAllied(
|
gameState.provinces(provinceId),
|
||||||
gameState.provinces(provinceId),
|
gameState
|
||||||
gameState
|
|
||||||
)
|
|
||||||
)(
|
|
||||||
FreeForAllDecisionAvailableCommand(
|
|
||||||
provinceId = provinceId,
|
|
||||||
armies = armyStats(factionId, provinceId, gameState),
|
|
||||||
actingUnits = hostileArmyGroup.armies
|
|
||||||
.flatMap(_.getArmy.units)
|
|
||||||
.map(cu =>
|
|
||||||
ExpandedCombatUnitUtils.expandedCombatUnit(cu, gameState)
|
|
||||||
),
|
|
||||||
availableDecisions = decisionOptions(hostileArmyGroup.armies)
|
|
||||||
)
|
)
|
||||||
|
)(
|
||||||
|
FreeForAllDecisionAvailableCommand(
|
||||||
|
provinceId = provinceId,
|
||||||
|
armies = armyStats(factionId, provinceId, gameState),
|
||||||
|
actingUnits = hostileArmyGroup.armies
|
||||||
|
.flatMap(_.getArmy.units)
|
||||||
|
.map(cu => ExpandedCombatUnitUtils.expandedCombatUnit(cu, gameState)),
|
||||||
|
availableDecisions = decisionOptions(hostileArmyGroup.armies)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-18
@@ -1,10 +1,7 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{ExpandedCapturedHero, HandleCapturedHeroAvailableCommand}
|
||||||
ExpandedCapturedHero,
|
|
||||||
HandleCapturedHeroAvailableCommand
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.api.command.util.captured_hero_option.CapturedHeroOption.{
|
import net.eagle0.eagle.api.command.util.captured_hero_option.CapturedHeroOption.{
|
||||||
EXECUTE_CAPTURED_HERO_OPTION,
|
EXECUTE_CAPTURED_HERO_OPTION,
|
||||||
EXILE_CAPTURED_HERO_OPTION,
|
EXILE_CAPTURED_HERO_OPTION,
|
||||||
@@ -17,17 +14,15 @@ import net.eagle0.eagle.internal.unaffiliated_hero.CapturedHero
|
|||||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||||
import net.eagle0.eagle.library.util.view_filters.HeroViewFilter
|
import net.eagle0.eagle.library.util.view_filters.HeroViewFilter
|
||||||
|
|
||||||
object AvailableHandleCapturedHeroCommandFactory
|
object AvailableHandleCapturedHeroCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
private val optionsWithoutRecruit = {
|
private val optionsWithoutRecruit =
|
||||||
Vector(
|
Vector(
|
||||||
IMPRISON_CAPTURED_HERO_OPTION,
|
IMPRISON_CAPTURED_HERO_OPTION,
|
||||||
EXILE_CAPTURED_HERO_OPTION,
|
EXILE_CAPTURED_HERO_OPTION,
|
||||||
EXECUTE_CAPTURED_HERO_OPTION
|
EXECUTE_CAPTURED_HERO_OPTION
|
||||||
)
|
)
|
||||||
}
|
private val optionsWithRecruit =
|
||||||
private val optionsWithRecruit =
|
|
||||||
RECRUIT_CAPTURED_HERO_OPTION +: optionsWithoutRecruit
|
RECRUIT_CAPTURED_HERO_OPTION +: optionsWithoutRecruit
|
||||||
private val optionsForFactionLeader = Vector(
|
private val optionsForFactionLeader = Vector(
|
||||||
IMPRISON_CAPTURED_HERO_OPTION,
|
IMPRISON_CAPTURED_HERO_OPTION,
|
||||||
@@ -72,14 +67,11 @@ object AvailableHandleCapturedHeroCommandFactory
|
|||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[HandleCapturedHeroAvailableCommand] =
|
): Option[HandleCapturedHeroAvailableCommand] =
|
||||||
capturedHeroes
|
capturedHeroes
|
||||||
.find(ch =>
|
.find(ch => ch.recruitmentRefusedMessageId.nonEmpty || ch.messageId.nonEmpty)
|
||||||
ch.recruitmentRefusedMessageId.nonEmpty || ch.messageId.nonEmpty
|
|
||||||
)
|
|
||||||
.map { capturedHero =>
|
.map { capturedHero =>
|
||||||
HandleCapturedHeroAvailableCommand(
|
HandleCapturedHeroAvailableCommand(
|
||||||
actingProvinceId = provinceId,
|
actingProvinceId = provinceId,
|
||||||
availableHeroes =
|
availableHeroes = Vector(expandedCapturedHero(gameState, capturedHero))
|
||||||
Vector(expandedCapturedHero(gameState, capturedHero))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,15 +79,15 @@ object AvailableHandleCapturedHeroCommandFactory
|
|||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[HandleCapturedHeroAvailableCommand] = {
|
): Option[HandleCapturedHeroAvailableCommand] =
|
||||||
// If we've already attempted to recruit one of the heroes, only show available commands for that one
|
// If we've already attempted to recruit one of the heroes, only show available commands for that one
|
||||||
gameState
|
gameState
|
||||||
.provinces(provinceId)
|
.provinces(provinceId)
|
||||||
.capturedHeroes
|
.capturedHeroes
|
||||||
.partition(_.recruitmentAttempted) match {
|
.partition(_.recruitmentAttempted) match {
|
||||||
case (Vector(), Vector()) => None
|
case (Vector(), Vector()) => None
|
||||||
// If the top hero in either set doesn't have a message, return nothing, otherwise return just the one option
|
// If the top hero in either set doesn't have a message, return nothing, otherwise return just the one option
|
||||||
case (Vector(), notRecruited) =>
|
case (Vector(), notRecruited) =>
|
||||||
firstOption(
|
firstOption(
|
||||||
gameState = gameState,
|
gameState = gameState,
|
||||||
capturedHeroes = notRecruited.toVector,
|
capturedHeroes = notRecruited.toVector,
|
||||||
@@ -108,5 +100,4 @@ object AvailableHandleCapturedHeroCommandFactory
|
|||||||
provinceId = provinceId
|
provinceId = provinceId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -7,17 +7,14 @@ import net.eagle0.eagle.internal.province.Province
|
|||||||
import net.eagle0.eagle.library.settings.MinVigorForCrackDown
|
import net.eagle0.eagle.library.settings.MinVigorForCrackDown
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableHandleRiotCrackDownCommandFactory
|
object AvailableHandleRiotCrackDownCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
def crackDownHeroIds(
|
def crackDownHeroIds(
|
||||||
province: Province,
|
province: Province,
|
||||||
gameState: GameState
|
gameState: GameState
|
||||||
): Vector[HeroId] =
|
): Vector[HeroId] =
|
||||||
province.rulingFactionHeroIds
|
province.rulingFactionHeroIds
|
||||||
.filter(hid =>
|
.filter(hid => gameState.heroes(hid).vigor >= MinVigorForCrackDown.doubleValue)
|
||||||
gameState.heroes(hid).vigor >= MinVigorForCrackDown.doubleValue
|
|
||||||
)
|
|
||||||
.toVector
|
.toVector
|
||||||
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
@@ -26,7 +23,7 @@ object AvailableHandleRiotCrackDownCommandFactory
|
|||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[HandleRiotCrackDownAvailableCommand] = {
|
): Option[HandleRiotCrackDownAvailableCommand] = {
|
||||||
val province = gameState.provinces(provinceId)
|
val province = gameState.provinces(provinceId)
|
||||||
val heroIds = crackDownHeroIds(province, gameState)
|
val heroIds = crackDownHeroIds(province, gameState)
|
||||||
|
|
||||||
Option.when(
|
Option.when(
|
||||||
LegacyProvinceUtils
|
LegacyProvinceUtils
|
||||||
|
|||||||
+1
-2
@@ -5,8 +5,7 @@ import net.eagle0.eagle.api.available_command.HandleRiotDoNothingAvailableComman
|
|||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableHandleRiotDoNothingCommandFactory
|
object AvailableHandleRiotDoNothingCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
|
|||||||
+1
-2
@@ -6,8 +6,7 @@ import net.eagle0.eagle.internal.game_state.GameState
|
|||||||
import net.eagle0.eagle.library.settings.{RiotMaxFood, RiotMaxGold}
|
import net.eagle0.eagle.library.settings.{RiotMaxFood, RiotMaxGold}
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableHandleRiotGiveCommandFactory
|
object AvailableHandleRiotGiveCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
|
|||||||
+2
-6
@@ -1,10 +1,7 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{EligibleGift, HeroGiftAvailableCommand}
|
||||||
EligibleGift,
|
|
||||||
HeroGiftAvailableCommand
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.province.Province
|
import net.eagle0.eagle.internal.province.Province
|
||||||
import net.eagle0.eagle.library.settings.MaxGiftGold
|
import net.eagle0.eagle.library.settings.MaxGiftGold
|
||||||
@@ -31,7 +28,7 @@ object AvailableHeroGiftCommandFactory extends AvailableCommandsFactoryForType {
|
|||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[HeroGiftAvailableCommand] = {
|
): Option[HeroGiftAvailableCommand] =
|
||||||
consideredProvinces(gameState, factionId, provinceId)
|
consideredProvinces(gameState, factionId, provinceId)
|
||||||
.filterNot(_.gold == 0)
|
.filterNot(_.gold == 0)
|
||||||
.flatMap(p => eligibleRecipients(p, gameState)) match {
|
.flatMap(p => eligibleRecipients(p, gameState)) match {
|
||||||
@@ -44,5 +41,4 @@ object AvailableHeroGiftCommandFactory extends AvailableCommandsFactoryForType {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-9
@@ -2,12 +2,7 @@ package net.eagle0.eagle.library.actions.availability
|
|||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.ImproveAvailableCommand
|
import net.eagle0.eagle.api.available_command.ImproveAvailableCommand
|
||||||
import net.eagle0.eagle.common.improvement_type.ImprovementType.{
|
import net.eagle0.eagle.common.improvement_type.ImprovementType.{AGRICULTURE, DEVASTATION, ECONOMY, INFRASTRUCTURE}
|
||||||
AGRICULTURE,
|
|
||||||
DEVASTATION,
|
|
||||||
ECONOMY,
|
|
||||||
INFRASTRUCTURE
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.profession.Profession.ENGINEER
|
import net.eagle0.eagle.common.profession.Profession.ENGINEER
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.hero.Hero
|
import net.eagle0.eagle.internal.hero.Hero
|
||||||
@@ -42,14 +37,14 @@ object AvailableImproveCommandsFactory extends AvailableCommandsFactoryForType {
|
|||||||
.filterNot(_.vigor < MinVigorForImprove.doubleValue)
|
.filterNot(_.vigor < MinVigorForImprove.doubleValue)
|
||||||
|
|
||||||
val availableTypes =
|
val availableTypes =
|
||||||
Option.when(province.economy < IMPROVEMENT_MAX) { ECONOMY } ++
|
Option.when(province.economy < IMPROVEMENT_MAX)(ECONOMY) ++
|
||||||
Option.when(province.agriculture < IMPROVEMENT_MAX) { AGRICULTURE } ++
|
Option.when(province.agriculture < IMPROVEMENT_MAX)(AGRICULTURE) ++
|
||||||
Option.when(province.infrastructure < IMPROVEMENT_MAX) {
|
Option.when(province.infrastructure < IMPROVEMENT_MAX) {
|
||||||
INFRASTRUCTURE
|
INFRASTRUCTURE
|
||||||
} ++
|
} ++
|
||||||
Option.when(
|
Option.when(
|
||||||
province.economyDevastation > 0 || province.agricultureDevastation > 0 || province.infrastructureDevastation > 0
|
province.economyDevastation > 0 || province.agricultureDevastation > 0 || province.infrastructureDevastation > 0
|
||||||
) { DEVASTATION }
|
)(DEVASTATION)
|
||||||
|
|
||||||
Option.when(availableHeroes.nonEmpty && availableTypes.nonEmpty) {
|
Option.when(availableHeroes.nonEmpty && availableTypes.nonEmpty) {
|
||||||
ImproveAvailableCommand(
|
ImproveAvailableCommand(
|
||||||
|
|||||||
+2
-5
@@ -7,8 +7,7 @@ import net.eagle0.eagle.common.province_order_type.ProvinceOrderType.*
|
|||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||||
|
|
||||||
object AvailableIssueOrdersCommandFactory
|
object AvailableIssueOrdersCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
@@ -28,9 +27,7 @@ object AvailableIssueOrdersCommandFactory
|
|||||||
.filter(_.rulingFactionId.contains(factionId))
|
.filter(_.rulingFactionId.contains(factionId))
|
||||||
.toVector
|
.toVector
|
||||||
.sortBy(_.name)
|
.sortBy(_.name)
|
||||||
.map(p =>
|
.map(p => ProvinceOrders(provinceId = p.id, orders = p.provinceOrders)),
|
||||||
ProvinceOrders(provinceId = p.id, orders = p.provinceOrders)
|
|
||||||
),
|
|
||||||
availableOrders = Vector(DEVELOP, MOBILIZE, EXPAND, ENTRUST)
|
availableOrders = Vector(DEVELOP, MOBILIZE, EXPAND, ENTRUST)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-9
@@ -1,10 +1,7 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
import net.eagle0.common.MoreOption
|
import net.eagle0.common.MoreOption
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{ManagePrisonersAvailableCommand, PrisonerToManage}
|
||||||
ManagePrisonersAvailableCommand,
|
|
||||||
PrisonerToManage
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.api.command.util.prisoner_management_type.{
|
import net.eagle0.eagle.api.command.util.prisoner_management_type.{
|
||||||
PrisonerManagementOption,
|
PrisonerManagementOption,
|
||||||
PrisonerManagementOptionExecute,
|
PrisonerManagementOptionExecute,
|
||||||
@@ -17,8 +14,7 @@ import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.UNAFF
|
|||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
||||||
|
|
||||||
object AvailableManagePrisonersCommandFactory
|
object AvailableManagePrisonersCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
private def availableMoveToProvinceIds(
|
private def availableMoveToProvinceIds(
|
||||||
fid: FactionId,
|
fid: FactionId,
|
||||||
@@ -51,9 +47,7 @@ object AvailableManagePrisonersCommandFactory
|
|||||||
_.rulingFactionId.contains(factionWithThisPrisonerAsLeader.get.id)
|
_.rulingFactionId.contains(factionWithThisPrisonerAsLeader.get.id)
|
||||||
)
|
)
|
||||||
)(
|
)(
|
||||||
PrisonerManagementOptionReturn(toFactionId =
|
PrisonerManagementOptionReturn(toFactionId = factionWithThisPrisonerAsLeader.get.id)
|
||||||
factionWithThisPrisonerAsLeader.get.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.toVector
|
.toVector
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+7
-19
@@ -10,19 +10,9 @@ import net.eagle0.eagle.api.available_command.{
|
|||||||
import net.eagle0.eagle.api.command.util.battalion_with_food_cost.BattalionWithFoodCost
|
import net.eagle0.eagle.api.command.util.battalion_with_food_cost.BattalionWithFoodCost
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.province.Province
|
import net.eagle0.eagle.internal.province.Province
|
||||||
import net.eagle0.eagle.library.settings.{
|
import net.eagle0.eagle.library.settings.{MaxCombatUnitCountPerSide, MinVigorForMarch}
|
||||||
MaxCombatUnitCountPerSide,
|
import net.eagle0.eagle.library.util.{BattalionSuitability, LegacyBattalionUtils, ShardokMapInfo}
|
||||||
MinVigorForMarch
|
import net.eagle0.eagle.library.util.command_choice_helpers.{CombatUnitSelector, MarchSuppliesHelpers}
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.{
|
|
||||||
BattalionSuitability,
|
|
||||||
LegacyBattalionUtils,
|
|
||||||
ShardokMapInfo
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
|
||||||
CombatUnitSelector,
|
|
||||||
MarchSuppliesHelpers
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
@@ -42,9 +32,9 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
|||||||
.get
|
.get
|
||||||
.startingPositionIndex
|
.startingPositionIndex
|
||||||
|
|
||||||
val mapInfo = ShardokMapInfo.shardokMapInfos(destinationProvince.id)
|
val mapInfo = ShardokMapInfo.shardokMapInfos(destinationProvince.id)
|
||||||
val destinationCastleCount = mapInfo.castleCount
|
val destinationCastleCount = mapInfo.castleCount
|
||||||
val requiresRiverCrossing = mapInfo.waterCrossingRequirementsByPosition(
|
val requiresRiverCrossing = mapInfo.waterCrossingRequirementsByPosition(
|
||||||
startingPositionIndex
|
startingPositionIndex
|
||||||
) >= 10
|
) >= 10
|
||||||
|
|
||||||
@@ -73,7 +63,7 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
|||||||
.min(availableBattalions.size)
|
.min(availableBattalions.size)
|
||||||
.min(originProvince.rulingFactionHeroIds.size - 2)
|
.min(originProvince.rulingFactionHeroIds.size - 2)
|
||||||
.min(MaxCombatUnitCountPerSide.intValue)
|
.min(MaxCombatUnitCountPerSide.intValue)
|
||||||
val recommendedConfig =
|
val recommendedConfig =
|
||||||
Option.when(recommendedSendCount > 0) {
|
Option.when(recommendedSendCount > 0) {
|
||||||
val units = CombatUnitSelector.selectedCombatBattalions(
|
val units = CombatUnitSelector.selectedCombatBattalions(
|
||||||
desiredCount = recommendedSendCount,
|
desiredCount = recommendedSendCount,
|
||||||
@@ -128,9 +118,7 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
|||||||
): Option[MarchAvailableCommand] =
|
): Option[MarchAvailableCommand] =
|
||||||
LegacyHeroUtils
|
LegacyHeroUtils
|
||||||
.consideredProvinces(gameState, factionId, provinceId)
|
.consideredProvinces(gameState, factionId, provinceId)
|
||||||
.flatMap(p =>
|
.flatMap(p => oneOriginProvinceCommand(gameState = gameState, originProvince = p)) match {
|
||||||
oneOriginProvinceCommand(gameState = gameState, originProvince = p)
|
|
||||||
) match {
|
|
||||||
case items if items.isEmpty => None
|
case items if items.isEmpty => None
|
||||||
case opcs =>
|
case opcs =>
|
||||||
Some(
|
Some(
|
||||||
|
|||||||
+16
-22
@@ -1,16 +1,12 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{OrganizeTroopsAvailableCommand, TroopCost}
|
||||||
OrganizeTroopsAvailableCommand,
|
|
||||||
TroopCost
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.api.available_command.OrganizeTroopsAvailableCommand.BattalionTypeStatus
|
import net.eagle0.eagle.api.available_command.OrganizeTroopsAvailableCommand.BattalionTypeStatus
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableOrganizeTroopsCommandsFactory
|
object AvailableOrganizeTroopsCommandsFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
val maxBattalions = 10 // FIXME
|
val maxBattalions = 10 // FIXME
|
||||||
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
@@ -20,20 +16,19 @@ object AvailableOrganizeTroopsCommandsFactory
|
|||||||
): Option[OrganizeTroopsAvailableCommand] = {
|
): Option[OrganizeTroopsAvailableCommand] = {
|
||||||
val province = gameState.provinces(provinceId)
|
val province = gameState.provinces(provinceId)
|
||||||
|
|
||||||
val availableTypeStatuses = gameState.battalionTypes.toVector
|
val availableTypeStatuses = gameState.battalionTypes.toVector.map { bt =>
|
||||||
.map { bt =>
|
BattalionTypeStatus(
|
||||||
BattalionTypeStatus(
|
typeId = bt.typeId,
|
||||||
typeId = bt.typeId,
|
minimumAgriculture = bt.minimumAgriculture,
|
||||||
minimumAgriculture = bt.minimumAgriculture,
|
minimumEconomy = bt.minimumEconomy,
|
||||||
minimumEconomy = bt.minimumEconomy,
|
meetsRequirements = LegacyProvinceUtils
|
||||||
meetsRequirements = LegacyProvinceUtils
|
.availableBattalionTypeIds(
|
||||||
.availableBattalionTypeIds(
|
province,
|
||||||
province,
|
gameState.battalionTypes.toVector
|
||||||
gameState.battalionTypes.toVector
|
)
|
||||||
)
|
.contains(bt.typeId)
|
||||||
.contains(bt.typeId)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
.sortBy(_.typeId.value)
|
.sortBy(_.typeId.value)
|
||||||
|
|
||||||
val troopCosts = gameState.battalionTypes.toVector.map(t =>
|
val troopCosts = gameState.battalionTypes.toVector.map(t =>
|
||||||
@@ -44,8 +39,7 @@ object AvailableOrganizeTroopsCommandsFactory
|
|||||||
)
|
)
|
||||||
|
|
||||||
if troopCosts.isEmpty then return None
|
if troopCosts.isEmpty then return None
|
||||||
if troopCosts.map(_.costPerTroop).min.ceil.toInt > province.gold then
|
if troopCosts.map(_.costPerTroop).min.ceil.toInt > province.gold then return None
|
||||||
return None
|
|
||||||
|
|
||||||
val existingEligibleBattalions = province.battalionIds
|
val existingEligibleBattalions = province.battalionIds
|
||||||
.map(bid => gameState.battalions(bid))
|
.map(bid => gameState.battalions(bid))
|
||||||
|
|||||||
+8
-12
@@ -1,9 +1,6 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
|
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{OneProvincePleaseRecruitMe, PleaseRecruitMeAvailableCommand}
|
||||||
OneProvincePleaseRecruitMe,
|
|
||||||
PleaseRecruitMeAvailableCommand
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.province.Province
|
import net.eagle0.eagle.internal.province.Province
|
||||||
import net.eagle0.eagle.library.settings.MinSupportForTaxes
|
import net.eagle0.eagle.library.settings.MinSupportForTaxes
|
||||||
@@ -19,20 +16,19 @@ object AvailablePleaseRecruitMeCommandFactory {
|
|||||||
): Option[OneProvincePleaseRecruitMe] = {
|
): Option[OneProvincePleaseRecruitMe] = {
|
||||||
val availableHeroes = for {
|
val availableHeroes = for {
|
||||||
targetUH <- p.unaffiliatedHeroes.filter(uh =>
|
targetUH <- p.unaffiliatedHeroes.filter(uh =>
|
||||||
LegacyUnaffiliatedHeroUtils.willPleaseRecruitMe(
|
LegacyUnaffiliatedHeroUtils.willPleaseRecruitMe(
|
||||||
gameState = gs,
|
gameState = gs,
|
||||||
factionId = fid,
|
factionId = fid,
|
||||||
unaffiliatedHero = uh
|
unaffiliatedHero = uh
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} yield {
|
} yield
|
||||||
// FIXME: We should be generating the LLM request and its textID here instead of earlier in the round
|
// FIXME: We should be generating the LLM request and its textID here instead of earlier in the round
|
||||||
ExpandedUnaffiliatedHeroUtils
|
ExpandedUnaffiliatedHeroUtils
|
||||||
.expandedUnaffiliatedHero(
|
.expandedUnaffiliatedHero(
|
||||||
gs = gs,
|
gs = gs,
|
||||||
uh = targetUH
|
uh = targetUH
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
Option.when(availableHeroes.nonEmpty)(
|
Option.when(availableHeroes.nonEmpty)(
|
||||||
OneProvincePleaseRecruitMe(
|
OneProvincePleaseRecruitMe(
|
||||||
|
|||||||
+2
-4
@@ -8,8 +8,7 @@ import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
|||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
import net.eagle0.eagle.library.util.recruitment_odds.LegacyRecruitmentOdds
|
import net.eagle0.eagle.library.util.recruitment_odds.LegacyRecruitmentOdds
|
||||||
|
|
||||||
object AvailableRecruitHeroesCommandFactory
|
object AvailableRecruitHeroesCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
@@ -21,8 +20,7 @@ object AvailableRecruitHeroesCommandFactory
|
|||||||
if !province.rulerIsTraveling then return None
|
if !province.rulerIsTraveling then return None
|
||||||
|
|
||||||
val faction = gameState.factions(factionId)
|
val faction = gameState.factions(factionId)
|
||||||
if !LegacyProvinceUtils.ruledByFactionLeader(province, gameState) then
|
if !LegacyProvinceUtils.ruledByFactionLeader(province, gameState) then return None
|
||||||
return None
|
|
||||||
|
|
||||||
val expandedRecruitable = province.unaffiliatedHeroes
|
val expandedRecruitable = province.unaffiliatedHeroes
|
||||||
.filterNot(uh => LegacyFactionUtils.isFactionLeader(uh.heroId, gameState))
|
.filterNot(uh => LegacyFactionUtils.isFactionLeader(uh.heroId, gameState))
|
||||||
|
|||||||
+6
-10
@@ -1,9 +1,6 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
import net.eagle0.eagle.api.available_command.ResolveAllianceOfferAvailableCommand
|
import net.eagle0.eagle.api.available_command.ResolveAllianceOfferAvailableCommand
|
||||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
import net.eagle0.eagle.common.diplomacy_offer.{AllianceOfferDetails, DiplomacyOffer}
|
||||||
AllianceOfferDetails,
|
|
||||||
DiplomacyOffer
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.FactionId
|
import net.eagle0.eagle.FactionId
|
||||||
@@ -52,12 +49,11 @@ object AvailableResolveAllianceOfferCommandFactory {
|
|||||||
messengerHeroId = messengerHeroId,
|
messengerHeroId = messengerHeroId,
|
||||||
messengerOriginProvinceId = messengerOriginProvinceId,
|
messengerOriginProvinceId = messengerOriginProvinceId,
|
||||||
status = status,
|
status = status,
|
||||||
eligibleStatuses =
|
eligibleStatuses = EligibleDiplomacyStatuses.acceptRejectStatuses ++ EligibleDiplomacyStatuses
|
||||||
EligibleDiplomacyStatuses.acceptRejectStatuses ++ EligibleDiplomacyStatuses
|
.maybeImprisonStatus(
|
||||||
.maybeImprisonStatus(
|
actingFactionId = factionId,
|
||||||
actingFactionId = factionId,
|
gameState = gameState
|
||||||
gameState = gameState
|
),
|
||||||
),
|
|
||||||
offerTextId = offerTextId
|
offerTextId = offerTextId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-4
@@ -1,9 +1,6 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
import net.eagle0.eagle.api.available_command.ResolveBreakAllianceAvailableCommand
|
import net.eagle0.eagle.api.available_command.ResolveBreakAllianceAvailableCommand
|
||||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
import net.eagle0.eagle.common.diplomacy_offer.{BreakAllianceDetails, DiplomacyOffer}
|
||||||
BreakAllianceDetails,
|
|
||||||
DiplomacyOffer
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus
|
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus
|
||||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||||
|
|||||||
+1
-4
@@ -1,9 +1,6 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
import net.eagle0.eagle.api.available_command.ResolveInvitationAvailableCommand
|
import net.eagle0.eagle.api.available_command.ResolveInvitationAvailableCommand
|
||||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
import net.eagle0.eagle.common.diplomacy_offer.{DiplomacyOffer, InvitationDetails}
|
||||||
DiplomacyOffer,
|
|
||||||
InvitationDetails
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||||
DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||||
|
|||||||
+1
-5
@@ -1,10 +1,6 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
import net.eagle0.eagle.api.available_command.ResolveRansomOfferAvailableCommand
|
import net.eagle0.eagle.api.available_command.ResolveRansomOfferAvailableCommand
|
||||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
import net.eagle0.eagle.common.diplomacy_offer.{DiplomacyOffer, InvitationDetails, RansomOfferDetails}
|
||||||
DiplomacyOffer,
|
|
||||||
InvitationDetails,
|
|
||||||
RansomOfferDetails
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||||
DIPLOMACY_OFFER_STATUS_REJECTED,
|
DIPLOMACY_OFFER_STATUS_REJECTED,
|
||||||
DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||||
|
|||||||
+16
-22
@@ -1,17 +1,13 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
|
|
||||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||||
import net.eagle0.eagle.api.available_command.{
|
import net.eagle0.eagle.api.available_command.{ResolveTributeAvailableCommand, TributeAndFaction}
|
||||||
ResolveTributeAvailableCommand,
|
|
||||||
TributeAndFaction
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.internal.army.{HostileArmyGroup, HostileArmyGroupStatus}
|
import net.eagle0.eagle.internal.army.{HostileArmyGroup, HostileArmyGroupStatus}
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.internal.province.Province
|
import net.eagle0.eagle.internal.province.Province
|
||||||
import net.eagle0.eagle.library.util.ArmyUtils
|
import net.eagle0.eagle.library.util.ArmyUtils
|
||||||
|
|
||||||
object AvailableResolveTributeCommandsFactory
|
object AvailableResolveTributeCommandsFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
private def makeResolveTributeCommand(
|
private def makeResolveTributeCommand(
|
||||||
province: Province,
|
province: Province,
|
||||||
@@ -25,22 +21,20 @@ object AvailableResolveTributeCommandsFactory
|
|||||||
Option.when(incomingTributeDemands.nonEmpty) {
|
Option.when(incomingTributeDemands.nonEmpty) {
|
||||||
ResolveTributeAvailableCommand(
|
ResolveTributeAvailableCommand(
|
||||||
actingProvinceId = province.id,
|
actingProvinceId = province.id,
|
||||||
demands = incomingTributeDemands
|
demands = incomingTributeDemands.map {
|
||||||
.map {
|
case ag @ HostileArmyGroup(
|
||||||
case ag @ HostileArmyGroup(
|
fid: FactionId,
|
||||||
fid: FactionId,
|
_,
|
||||||
_,
|
status: HostileArmyGroupStatus,
|
||||||
status: HostileArmyGroupStatus,
|
_ /* unknownFieldSet */
|
||||||
_ /* unknownFieldSet */
|
) =>
|
||||||
) =>
|
TributeAndFaction(
|
||||||
TributeAndFaction(
|
demandingFactionId = fid,
|
||||||
demandingFactionId = fid,
|
tributeDemanded = status.asMessage.getTributeDemanded.tributeAmount,
|
||||||
tributeDemanded =
|
heroCount = ArmyUtils.heroCount(ag),
|
||||||
status.asMessage.getTributeDemanded.tributeAmount,
|
troopCount = ArmyUtils.troopCount(ag, gs)
|
||||||
heroCount = ArmyUtils.heroCount(ag),
|
)
|
||||||
troopCount = ArmyUtils.troopCount(ag, gs)
|
},
|
||||||
)
|
|
||||||
},
|
|
||||||
availableGold = province.gold,
|
availableGold = province.gold,
|
||||||
availableFood = province.food
|
availableFood = province.food
|
||||||
)
|
)
|
||||||
|
|||||||
+7
-11
@@ -1,9 +1,6 @@
|
|||||||
package net.eagle0.eagle.library.actions.availability
|
package net.eagle0.eagle.library.actions.availability
|
||||||
import net.eagle0.eagle.api.available_command.ResolveTruceOfferAvailableCommand
|
import net.eagle0.eagle.api.available_command.ResolveTruceOfferAvailableCommand
|
||||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
import net.eagle0.eagle.common.diplomacy_offer.{DiplomacyOffer, TruceOfferDetails}
|
||||||
DiplomacyOffer,
|
|
||||||
TruceOfferDetails
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.settings.TruceMonths
|
import net.eagle0.eagle.library.settings.TruceMonths
|
||||||
@@ -59,13 +56,12 @@ object AvailableResolveTruceOfferCommandFactory {
|
|||||||
messengerHeroId = messengerHeroId,
|
messengerHeroId = messengerHeroId,
|
||||||
messengerOriginProvinceId = messengerOriginProvinceId,
|
messengerOriginProvinceId = messengerOriginProvinceId,
|
||||||
status = status,
|
status = status,
|
||||||
eligibleStatuses =
|
eligibleStatuses = (EligibleDiplomacyStatuses.acceptRejectStatuses ++ EligibleDiplomacyStatuses
|
||||||
(EligibleDiplomacyStatuses.acceptRejectStatuses ++ EligibleDiplomacyStatuses
|
.maybeImprisonStatus(
|
||||||
.maybeImprisonStatus(
|
actingFactionId = factionId,
|
||||||
actingFactionId = factionId,
|
gameState = gameState
|
||||||
gameState = gameState
|
)
|
||||||
)
|
.toVector).toVector,
|
||||||
.toVector).toVector,
|
|
||||||
offerTextId = offerTextId
|
offerTextId = offerTextId
|
||||||
)
|
)
|
||||||
}.toVector
|
}.toVector
|
||||||
|
|||||||
+1
-2
@@ -7,8 +7,7 @@ import net.eagle0.eagle.library.settings.MinVigorForSendSupplies
|
|||||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableSendSuppliesCommandFactory
|
object AvailableSendSuppliesCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
override def availableCommand(
|
override def availableCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
|
|||||||
+2
-5
@@ -8,8 +8,7 @@ import net.eagle0.eagle.internal.province.Province
|
|||||||
import net.eagle0.eagle.library.settings.MinVigorForStartEpidemic
|
import net.eagle0.eagle.library.settings.MinVigorForStartEpidemic
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableStartEpidemicCommandFactory
|
object AvailableStartEpidemicCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
private def makeStartEpidemicCommand(
|
private def makeStartEpidemicCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
@@ -38,9 +37,7 @@ object AvailableStartEpidemicCommandFactory
|
|||||||
availableHeroIds = necromancersWithSufficientVigor
|
availableHeroIds = necromancersWithSufficientVigor
|
||||||
.sortBy(-_.vigor)
|
.sortBy(-_.vigor)
|
||||||
.map(_.id),
|
.map(_.id),
|
||||||
options = targetProvinceIds.map(pid =>
|
options = targetProvinceIds.map(pid => StartEpidemicOptions(targetProvinceId = pid)),
|
||||||
StartEpidemicOptions(targetProvinceId = pid)
|
|
||||||
),
|
|
||||||
actingProvinceId = province.id
|
actingProvinceId = province.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-5
@@ -7,17 +7,14 @@ import net.eagle0.eagle.internal.province.Province
|
|||||||
import net.eagle0.eagle.library.settings.MinVigorForSuppressBeasts
|
import net.eagle0.eagle.library.settings.MinVigorForSuppressBeasts
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
|
|
||||||
object AvailableSuppressBeastsCommandFactory
|
object AvailableSuppressBeastsCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
private def makeSuppressBeastsCommand(
|
private def makeSuppressBeastsCommand(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
province: Province
|
province: Province
|
||||||
): Option[SuppressBeastsAvailableCommand] = {
|
): Option[SuppressBeastsAvailableCommand] = {
|
||||||
val availableHeroIds = province.rulingFactionHeroIds
|
val availableHeroIds = province.rulingFactionHeroIds
|
||||||
.filter(hid =>
|
.filter(hid => gameState.heroes(hid).vigor >= MinVigorForSuppressBeasts.doubleValue)
|
||||||
gameState.heroes(hid).vigor >= MinVigorForSuppressBeasts.doubleValue
|
|
||||||
)
|
|
||||||
|
|
||||||
Option.when(
|
Option.when(
|
||||||
LegacyProvinceUtils.hasBeasts(province) && availableHeroIds.nonEmpty
|
LegacyProvinceUtils.hasBeasts(province) && availableHeroIds.nonEmpty
|
||||||
|
|||||||
+2
-6
@@ -4,13 +4,9 @@ import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
|
|||||||
import net.eagle0.eagle.api.available_command.SwearBrotherhoodAvailableCommand
|
import net.eagle0.eagle.api.available_command.SwearBrotherhoodAvailableCommand
|
||||||
import net.eagle0.eagle.api.available_command.SwearBrotherhoodAvailableCommand.HeroAndBackstory
|
import net.eagle0.eagle.api.available_command.SwearBrotherhoodAvailableCommand.HeroAndBackstory
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.settings.{
|
import net.eagle0.eagle.library.settings.{MaximumFactionLeaders, MinimumLoyaltyForSwearBrotherhood}
|
||||||
MaximumFactionLeaders,
|
|
||||||
MinimumLoyaltyForSwearBrotherhood
|
|
||||||
}
|
|
||||||
|
|
||||||
object AvailableSwearBrotherhoodCommandFactory
|
object AvailableSwearBrotherhoodCommandFactory extends AvailableCommandsFactoryForType {
|
||||||
extends AvailableCommandsFactoryForType {
|
|
||||||
|
|
||||||
def canHaveMoreLeaders(gameState: GameState, factionId: FactionId): Boolean =
|
def canHaveMoreLeaders(gameState: GameState, factionId: FactionId): Boolean =
|
||||||
gameState
|
gameState
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ object AvailableTrainCommandsFactory extends AvailableCommandsFactoryForType {
|
|||||||
factionId: FactionId,
|
factionId: FactionId,
|
||||||
provinceId: ProvinceId
|
provinceId: ProvinceId
|
||||||
): Option[TrainAvailableCommand] = {
|
): Option[TrainAvailableCommand] = {
|
||||||
val province = gameState.provinces(provinceId)
|
val province = gameState.provinces(provinceId)
|
||||||
val availableHeroes = province.rulingFactionHeroIds
|
val availableHeroes = province.rulingFactionHeroIds
|
||||||
.map(heroId => gameState.heroes(heroId))
|
.map(heroId => gameState.heroes(heroId))
|
||||||
.filter(_.vigor >= MinVigorForTrain.doubleValue)
|
.filter(_.vigor >= MinVigorForTrain.doubleValue)
|
||||||
|
|||||||
+1
-4
@@ -3,10 +3,7 @@ package net.eagle0.eagle.library.actions.availability
|
|||||||
import net.eagle0.eagle.api.command.util.expanded_combat_unit.ExpandedCombatUnit
|
import net.eagle0.eagle.api.command.util.expanded_combat_unit.ExpandedCombatUnit
|
||||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.util.view_filters.{
|
import net.eagle0.eagle.library.util.view_filters.{HeroViewFilter, LegacyBattalionViewFilter}
|
||||||
HeroViewFilter,
|
|
||||||
LegacyBattalionViewFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
object ExpandedCombatUnitUtils {
|
object ExpandedCombatUnitUtils {
|
||||||
def expandedCombatUnit(
|
def expandedCombatUnit(
|
||||||
|
|||||||
@@ -34,21 +34,21 @@ package object availability {
|
|||||||
.toMap
|
.toMap
|
||||||
|
|
||||||
val professionOrdering: Map[Profession, Int] = Map(
|
val professionOrdering: Map[Profession, Int] = Map(
|
||||||
PALADIN -> 1,
|
PALADIN -> 1,
|
||||||
ENGINEER -> 2,
|
ENGINEER -> 2,
|
||||||
RANGER -> 3,
|
RANGER -> 3,
|
||||||
MAGE -> 4,
|
MAGE -> 4,
|
||||||
NECROMANCER -> 5,
|
NECROMANCER -> 5,
|
||||||
CHAMPION -> 6,
|
CHAMPION -> 6,
|
||||||
NO_PROFESSION -> 7,
|
NO_PROFESSION -> 7,
|
||||||
UNKNOWN_PROFESSION -> 8
|
UNKNOWN_PROFESSION -> 8
|
||||||
)
|
)
|
||||||
|
|
||||||
val battalionTypeOrdering: Map[BattalionTypeId, Int] = Map(
|
val battalionTypeOrdering: Map[BattalionTypeId, Int] = Map(
|
||||||
HEAVY_CAVALRY -> 1,
|
HEAVY_CAVALRY -> 1,
|
||||||
HEAVY_INFANTRY -> 2,
|
HEAVY_INFANTRY -> 2,
|
||||||
LONGBOWMEN -> 3,
|
LONGBOWMEN -> 3,
|
||||||
LIGHT_CAVALRY -> 4,
|
LIGHT_CAVALRY -> 4,
|
||||||
LIGHT_INFANTRY -> 5
|
LIGHT_INFANTRY -> 5
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-57
@@ -7,11 +7,7 @@ import net.eagle0.eagle.library.util.province.ProvinceUtils
|
|||||||
import net.eagle0.eagle.library.util.ransom_validity.RansomValidity
|
import net.eagle0.eagle.library.util.ransom_validity.RansomValidity
|
||||||
import net.eagle0.eagle.library.util.ReturningHeroes
|
import net.eagle0.eagle.library.util.ReturningHeroes
|
||||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||||
import net.eagle0.eagle.model.action_result.concrete.{
|
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC, ChangedHeroC}
|
||||||
ActionResultC,
|
|
||||||
ChangedFactionC,
|
|
||||||
ChangedHeroC
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.action_result.types.{
|
import net.eagle0.eagle.model.action_result.types.{
|
||||||
FactionDestroyedResultType,
|
FactionDestroyedResultType,
|
||||||
FactionLeaderRemovedResultType,
|
FactionLeaderRemovedResultType,
|
||||||
@@ -25,10 +21,7 @@ import net.eagle0.eagle.model.state.faction.concrete.FactionC
|
|||||||
import net.eagle0.eagle.model.state.faction.FactionT
|
import net.eagle0.eagle.model.state.faction.FactionT
|
||||||
import net.eagle0.eagle.model.state.hero.HeroT
|
import net.eagle0.eagle.model.state.hero.HeroT
|
||||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||||
import net.eagle0.eagle.model.state.unaffiliated_hero.{
|
import net.eagle0.eagle.model.state.unaffiliated_hero.{RecruitmentInfo, UnaffiliatedHeroT}
|
||||||
RecruitmentInfo,
|
|
||||||
UnaffiliatedHeroT
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.state.unaffiliated_hero.concrete.UnaffiliatedHeroC
|
import net.eagle0.eagle.model.state.unaffiliated_hero.concrete.UnaffiliatedHeroC
|
||||||
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.Traveler
|
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.Traveler
|
||||||
import net.eagle0.eagle.model.state.Army
|
import net.eagle0.eagle.model.state.Army
|
||||||
@@ -95,10 +88,11 @@ case class CheckForFactionChangesAction(
|
|||||||
case (faction: FactionC, fr) =>
|
case (faction: FactionC, fr) =>
|
||||||
applyRemovedFactionResult(faction, fr)
|
applyRemovedFactionResult(faction, fr)
|
||||||
}
|
}
|
||||||
.continue { case (results, fr) =>
|
.continue {
|
||||||
maybeRansomInvalidResults(notDestroyed, fr).map { rirs =>
|
case (results, fr) =>
|
||||||
results ++ rirs
|
maybeRansomInvalidResults(notDestroyed, fr).map { rirs =>
|
||||||
}
|
results ++ rirs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.map { results =>
|
.map { results =>
|
||||||
results ++
|
results ++
|
||||||
@@ -114,15 +108,15 @@ case class CheckForFactionChangesAction(
|
|||||||
functionalRandom: FunctionalRandom
|
functionalRandom: FunctionalRandom
|
||||||
): RandomState[Vector[ReturningHeroes.ReturningResult]] =
|
): RandomState[Vector[ReturningHeroes.ReturningResult]] =
|
||||||
functionalRandom
|
functionalRandom
|
||||||
.nextMap(destroyedFaction.incomingDiplomacyOffers) { case (offer, fr) =>
|
.nextMap(destroyedFaction.incomingDiplomacyOffers) {
|
||||||
ReturningHeroes.heroesReturningToFaction(
|
case (offer, fr) =>
|
||||||
hids = Vector(offer.messengerHeroId),
|
ReturningHeroes.heroesReturningToFaction(
|
||||||
factionId = offer.originatingFactionId,
|
hids = Vector(offer.messengerHeroId),
|
||||||
originProvince =
|
factionId = offer.originatingFactionId,
|
||||||
provinces.find(_.id == offer.messengerOriginProvinceId).get,
|
originProvince = provinces.find(_.id == offer.messengerOriginProvinceId).get,
|
||||||
provinces = provinces,
|
provinces = provinces,
|
||||||
functionalRandom = fr
|
functionalRandom = fr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def applyRemovedFactionResult(
|
private def applyRemovedFactionResult(
|
||||||
@@ -140,13 +134,14 @@ case class CheckForFactionChangesAction(
|
|||||||
provinceId = p.id,
|
provinceId = p.id,
|
||||||
removedIncomingArmyIds = removedFactionArmies.map(_.id)
|
removedIncomingArmyIds = removedFactionArmies.map(_.id)
|
||||||
)
|
)
|
||||||
) { case (cp, ma) =>
|
) {
|
||||||
cp.withNewUnaffiliatedHeroes(
|
case (cp, ma) =>
|
||||||
armyToUnaffiliatedHeroes(ma.army)
|
cp.withNewUnaffiliatedHeroes(
|
||||||
).copy(
|
armyToUnaffiliatedHeroes(ma.army)
|
||||||
foodDelta = deltaAugment(cp.foodDelta, ma.supplies.food),
|
).copy(
|
||||||
goldDelta = deltaAugment(cp.goldDelta, ma.supplies.gold)
|
foodDelta = deltaAugment(cp.foodDelta, ma.supplies.food),
|
||||||
)
|
goldDelta = deltaAugment(cp.goldDelta, ma.supplies.gold)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -193,9 +188,7 @@ case class CheckForFactionChangesAction(
|
|||||||
): Option[ChangedFactionC] =
|
): Option[ChangedFactionC] =
|
||||||
Option.when(
|
Option.when(
|
||||||
faction.factionRelationships.exists(_.targetFactionId == otherFid)
|
faction.factionRelationships.exists(_.targetFactionId == otherFid)
|
||||||
|| faction.incomingDiplomacyOffers.exists(ioff =>
|
|| faction.incomingDiplomacyOffers.exists(ioff => ioff.originatingFactionId == otherFid)
|
||||||
ioff.originatingFactionId == otherFid
|
|
||||||
)
|
|
||||||
)(
|
)(
|
||||||
ChangedFactionC(
|
ChangedFactionC(
|
||||||
factionId = faction.id,
|
factionId = faction.id,
|
||||||
@@ -272,32 +265,30 @@ case class CheckForFactionChangesAction(
|
|||||||
functionalRandom: FunctionalRandom
|
functionalRandom: FunctionalRandom
|
||||||
): RandomState[Vector[ActionResultT]] =
|
): RandomState[Vector[ActionResultT]] =
|
||||||
functionalRandom
|
functionalRandom
|
||||||
.nextFlatMap(notDestroyedFactions.toVector) { case (faction, fr) =>
|
.nextFlatMap(notDestroyedFactions.toVector) {
|
||||||
fr.nextMap(
|
case (faction, fr) =>
|
||||||
faction.incomingDiplomacyOffers
|
fr.nextMap(
|
||||||
.collect { case diploOffer: RansomOffer =>
|
faction.incomingDiplomacyOffers.collect {
|
||||||
diploOffer
|
case diploOffer: RansomOffer =>
|
||||||
|
diploOffer
|
||||||
}
|
}
|
||||||
.filterNot(ransomOffer =>
|
.filterNot(ransomOffer => RansomValidity.isStillValid(ransomOffer, provinces))
|
||||||
RansomValidity.isStillValid(ransomOffer, provinces)
|
) {
|
||||||
)
|
case (ransomOffer, innerFr) =>
|
||||||
) { case (ransomOffer, innerFr) =>
|
// Ransom offers don't remove the hero from the province
|
||||||
// Ransom offers don't remove the hero from the province
|
RandomState(
|
||||||
RandomState(
|
newValue = ActionResultC(
|
||||||
newValue = ActionResultC(
|
actionResultType = RansomInvalidatedResultType,
|
||||||
actionResultType = RansomInvalidatedResultType,
|
changedFactions = Vector(
|
||||||
changedFactions = Vector(
|
ChangedFactionC(
|
||||||
ChangedFactionC(
|
factionId = faction.id,
|
||||||
factionId = faction.id,
|
removedIncomingDiplomacyOfferFactionIds = Vector(ransomOffer.originatingFactionId),
|
||||||
removedIncomingDiplomacyOfferFactionIds =
|
newIncomingDiplomacyOffers = Vector(ransomOffer.withStatus(Invalidated))
|
||||||
Vector(ransomOffer.originatingFactionId),
|
)
|
||||||
newIncomingDiplomacyOffers =
|
)
|
||||||
Vector(ransomOffer.withStatus(Invalidated))
|
),
|
||||||
)
|
nextRandom = innerFr
|
||||||
)
|
)
|
||||||
),
|
}
|
||||||
nextRandom = innerFr
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-10
@@ -54,10 +54,9 @@ case class CheckForFailedQuestsAction(
|
|||||||
// check for the province being owned by a different faction
|
// check for the province being owned by a different faction
|
||||||
else if imprisonedProvince.rulingFactionId != questFactionId then true
|
else if imprisonedProvince.rulingFactionId != questFactionId then true
|
||||||
// check for the hero being no longer a prisoner in the province
|
// check for the hero being no longer a prisoner in the province
|
||||||
else if !imprisonedProvince.unaffiliatedHeroes
|
else if !imprisonedProvince.unaffiliatedHeroes.exists { uh =>
|
||||||
.exists { uh =>
|
uh.heroId == prisonerHeroId && uh.unaffiliatedHeroType == Prisoner
|
||||||
uh.heroId == prisonerHeroId && uh.unaffiliatedHeroType == Prisoner
|
}
|
||||||
}
|
|
||||||
then true
|
then true
|
||||||
else false
|
else false
|
||||||
}
|
}
|
||||||
@@ -67,7 +66,7 @@ case class CheckForFailedQuestsAction(
|
|||||||
province: ProvinceT
|
province: ProvinceT
|
||||||
): Boolean =
|
): Boolean =
|
||||||
quest match {
|
quest match {
|
||||||
case DefeatFactionQuest(targetFactionId) =>
|
case DefeatFactionQuest(targetFactionId) =>
|
||||||
// Fails if the faction no longer exists, or if the ruling faction of this province has a truce with it
|
// Fails if the faction no longer exists, or if the ruling faction of this province has a truce with it
|
||||||
!factions.exists(_.id == targetFactionId) || province.rulingFactionId
|
!factions.exists(_.id == targetFactionId) || province.rulingFactionId
|
||||||
.exists(rulingFid =>
|
.exists(rulingFid =>
|
||||||
@@ -78,13 +77,13 @@ case class CheckForFailedQuestsAction(
|
|||||||
factions = factions
|
factions = factions
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case TruceWithFactionQuest(targetFactionId) =>
|
case TruceWithFactionQuest(targetFactionId) =>
|
||||||
// Fails if the faction no longer exists
|
// Fails if the faction no longer exists
|
||||||
!factions.exists(_.id == targetFactionId)
|
!factions.exists(_.id == targetFactionId)
|
||||||
case TruceCountQuest(truceCount) =>
|
case TruceCountQuest(truceCount) =>
|
||||||
// Fails if there are no longer enough factions to succeed
|
// Fails if there are no longer enough factions to succeed
|
||||||
factions.size <= truceCount
|
factions.size <= truceCount
|
||||||
case DismissSpecificVassalQuest(targetHeroId) =>
|
case DismissSpecificVassalQuest(targetHeroId) =>
|
||||||
// Fails if you swear brotherhood with the hero
|
// Fails if you swear brotherhood with the hero
|
||||||
FactionUtils.isFactionLeader(targetHeroId, factions)
|
FactionUtils.isFactionLeader(targetHeroId, factions)
|
||||||
case ExecutePrisonerQuest(prisonerHeroId, prisonerProvinceId) =>
|
case ExecutePrisonerQuest(prisonerHeroId, prisonerProvinceId) =>
|
||||||
@@ -93,7 +92,7 @@ case class CheckForFailedQuestsAction(
|
|||||||
prisonerHeroId = prisonerHeroId,
|
prisonerHeroId = prisonerHeroId,
|
||||||
prisonerProvinceId = prisonerProvinceId
|
prisonerProvinceId = prisonerProvinceId
|
||||||
)
|
)
|
||||||
case ExilePrisonerQuest(prisonerHeroId, prisonerProvinceId) =>
|
case ExilePrisonerQuest(prisonerHeroId, prisonerProvinceId) =>
|
||||||
isPrisonerManagementQuestFailed(
|
isPrisonerManagementQuestFailed(
|
||||||
questFactionId = province.rulingFactionId,
|
questFactionId = province.rulingFactionId,
|
||||||
prisonerHeroId = prisonerHeroId,
|
prisonerHeroId = prisonerHeroId,
|
||||||
@@ -117,7 +116,7 @@ case class CheckForFailedQuestsAction(
|
|||||||
prisonerHeroId = prisonerHeroId,
|
prisonerHeroId = prisonerHeroId,
|
||||||
prisonerProvinceId = prisonerProvinceId
|
prisonerProvinceId = prisonerProvinceId
|
||||||
)
|
)
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-31
@@ -1,29 +1,16 @@
|
|||||||
package net.eagle0.eagle.library.actions.impl.action
|
package net.eagle0.eagle.library.actions.impl.action
|
||||||
|
|
||||||
import net.eagle0.eagle.{
|
import net.eagle0.eagle.{BattalionId, FactionId, GameId, HeroId, ProvinceId, RoundId}
|
||||||
BattalionId,
|
|
||||||
FactionId,
|
|
||||||
GameId,
|
|
||||||
HeroId,
|
|
||||||
ProvinceId,
|
|
||||||
RoundId
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.common.battalion_type.BattalionType
|
import net.eagle0.eagle.common.battalion_type.BattalionType
|
||||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
|
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
|
||||||
import net.eagle0.eagle.library.settings.{
|
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, PortionOfCapacityForUpgradeBattalionQuest}
|
||||||
MinSupportForTaxes,
|
|
||||||
PortionOfCapacityForUpgradeBattalionQuest
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.province.ProvinceUtils
|
import net.eagle0.eagle.library.util.province.ProvinceUtils
|
||||||
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentUtils
|
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentUtils
|
||||||
import net.eagle0.eagle.library.util.BattalionTypeFinder
|
import net.eagle0.eagle.library.util.BattalionTypeFinder
|
||||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||||
import net.eagle0.eagle.model.state.date.Date
|
import net.eagle0.eagle.model.state.date.Date
|
||||||
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel.{
|
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel.{Ally, Hostile}
|
||||||
Ally,
|
|
||||||
Hostile
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.state.faction.FactionT
|
import net.eagle0.eagle.model.state.faction.FactionT
|
||||||
import net.eagle0.eagle.model.state.hero.HeroT
|
import net.eagle0.eagle.model.state.hero.HeroT
|
||||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||||
@@ -93,24 +80,24 @@ case class CheckForFulfilledQuestsAction(
|
|||||||
province: ProvinceT
|
province: ProvinceT
|
||||||
): Boolean =
|
): Boolean =
|
||||||
quest match {
|
quest match {
|
||||||
case q: ComponentQuest =>
|
case q: ComponentQuest =>
|
||||||
q.componentsFulfilled >= q.componentCount
|
q.componentsFulfilled >= q.componentCount
|
||||||
case _: DefeatFactionQuest =>
|
case _: DefeatFactionQuest =>
|
||||||
false // action quests
|
false // action quests
|
||||||
case DismissSpecificVassalQuest(targetHeroId) =>
|
case DismissSpecificVassalQuest(targetHeroId) =>
|
||||||
!getHero(targetHeroId).exists(
|
!getHero(targetHeroId).exists(
|
||||||
_.factionId
|
_.factionId
|
||||||
.contains(province.rulingFactionId.get)
|
.contains(province.rulingFactionId.get)
|
||||||
)
|
)
|
||||||
case _: ExecutePrisonerQuest =>
|
case _: ExecutePrisonerQuest =>
|
||||||
false // action quests
|
false // action quests
|
||||||
case _: ExilePrisonerQuest =>
|
case _: ExilePrisonerQuest =>
|
||||||
false // action quests
|
false // action quests
|
||||||
case _: ReleasePrisonerQuest =>
|
case _: ReleasePrisonerQuest =>
|
||||||
false // action quests
|
false // action quests
|
||||||
case ImproveAgricultureQuest(_: ProvinceId, desiredValue: Double) =>
|
case ImproveAgricultureQuest(_: ProvinceId, desiredValue: Double) =>
|
||||||
ProvinceUtils.effectiveAgriculture(province) >= desiredValue
|
ProvinceUtils.effectiveAgriculture(province) >= desiredValue
|
||||||
case ImproveEconomyQuest(_: ProvinceId, desiredValue: Double) =>
|
case ImproveEconomyQuest(_: ProvinceId, desiredValue: Double) =>
|
||||||
ProvinceUtils.effectiveEconomy(province) >= desiredValue
|
ProvinceUtils.effectiveEconomy(province) >= desiredValue
|
||||||
case ImproveInfrastructureQuest(
|
case ImproveInfrastructureQuest(
|
||||||
_: ProvinceId,
|
_: ProvinceId,
|
||||||
@@ -133,7 +120,7 @@ case class CheckForFulfilledQuestsAction(
|
|||||||
battalionTypeId
|
battalionTypeId
|
||||||
).capacity
|
).capacity
|
||||||
)
|
)
|
||||||
case GrandArmyQuest(totalTroopCount) =>
|
case GrandArmyQuest(totalTroopCount) =>
|
||||||
province.battalionIds
|
province.battalionIds
|
||||||
.flatMap(battalionWithId)
|
.flatMap(battalionWithId)
|
||||||
.map(_.size)
|
.map(_.size)
|
||||||
@@ -147,21 +134,19 @@ case class CheckForFulfilledQuestsAction(
|
|||||||
expansionProvince.rulingFactionId == province.rulingFactionId &&
|
expansionProvince.rulingFactionId == province.rulingFactionId &&
|
||||||
expansionProvince.support >= MinSupportForTaxes.doubleValue
|
expansionProvince.support >= MinSupportForTaxes.doubleValue
|
||||||
}
|
}
|
||||||
case WealthQuest(gold, food) =>
|
case WealthQuest(gold, food) =>
|
||||||
province.gold >= gold && province.food >= food
|
province.gold >= gold && province.food >= food
|
||||||
|
|
||||||
case AllianceQuest =>
|
case AllianceQuest =>
|
||||||
factionWithId(province.getRulingFactionId).get.factionRelationships
|
factionWithId(province.getRulingFactionId).get.factionRelationships
|
||||||
.exists(fr => fr.relationshipLevel == Ally)
|
.exists(fr => fr.relationshipLevel == Ally)
|
||||||
case TruceWithFactionQuest(targetFactionId) =>
|
case TruceWithFactionQuest(targetFactionId) =>
|
||||||
factionWithId(province.getRulingFactionId).get.factionRelationships
|
factionWithId(province.getRulingFactionId).get.factionRelationships
|
||||||
.exists(fr =>
|
.exists(fr => fr.targetFactionId == targetFactionId && fr.relationshipLevel != Hostile)
|
||||||
fr.targetFactionId == targetFactionId && fr.relationshipLevel != Hostile
|
case TruceCountQuest(truceCount) =>
|
||||||
)
|
|
||||||
case TruceCountQuest(truceCount) =>
|
|
||||||
factionWithId(province.getRulingFactionId).get.factionRelationships
|
factionWithId(province.getRulingFactionId).get.factionRelationships
|
||||||
.count(_.relationshipLevel != Hostile) >= truceCount
|
.count(_.relationshipLevel != Hostile) >= truceCount
|
||||||
case _ =>
|
case _ =>
|
||||||
throw new IllegalArgumentException(s"Unknown quest type: $quest")
|
throw new IllegalArgumentException(s"Unknown quest type: $quest")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-5
@@ -357,13 +357,13 @@ object ChronicleEventGenerator {
|
|||||||
factionId = factionId,
|
factionId = factionId,
|
||||||
provinceId = provinceId,
|
provinceId = provinceId,
|
||||||
reason = reason match {
|
reason = reason match {
|
||||||
case SHATTERED_ARMY_REASON_BLIZZARD =>
|
case SHATTERED_ARMY_REASON_BLIZZARD =>
|
||||||
SHATTERED_ARMY_EVENT_REASON_BLIZZARD
|
SHATTERED_ARMY_EVENT_REASON_BLIZZARD
|
||||||
case SHATTERED_ARMY_REASON_UNKNOWN_UNSPECIFIED =>
|
case SHATTERED_ARMY_REASON_UNKNOWN_UNSPECIFIED =>
|
||||||
SHATTERED_ARMY_EVENT_REASON_UNKNOWN_UNSPECIFIED
|
SHATTERED_ARMY_EVENT_REASON_UNKNOWN_UNSPECIFIED
|
||||||
case SHATTERED_ARMY_REASON_WITHDRAW =>
|
case SHATTERED_ARMY_REASON_WITHDRAW =>
|
||||||
SHATTERED_ARMY_EVENT_REASON_WITHDRAW
|
SHATTERED_ARMY_EVENT_REASON_WITHDRAW
|
||||||
case Unrecognized(value) =>
|
case Unrecognized(value) =>
|
||||||
ShatteredArmyEvent.Reason.Unrecognized(value)
|
ShatteredArmyEvent.Reason.Unrecognized(value)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -407,9 +407,8 @@ object ChronicleEventGenerator {
|
|||||||
def eventTextEntries(
|
def eventTextEntries(
|
||||||
gameHistory: GameHistory,
|
gameHistory: GameHistory,
|
||||||
since: Date
|
since: Date
|
||||||
): Vector[EventForChronicle] = {
|
): Vector[EventForChronicle] =
|
||||||
gameHistory.sinceDate(since).flatMap { (awrs: ActionWithResultingState) =>
|
gameHistory.sinceDate(since).flatMap { (awrs: ActionWithResultingState) =>
|
||||||
notificationEntries(awrs) ++ basicActionResultEntries(awrs)
|
notificationEntries(awrs) ++ basicActionResultEntries(awrs)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -43,18 +43,19 @@ case class EndAttackDecisionPhaseAction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val incomingAttacks = for {
|
private val incomingAttacks = for {
|
||||||
province <- provinces
|
province <- provinces
|
||||||
defendingFactionId <- province.rulingFactionId.toVector
|
defendingFactionId <- province.rulingFactionId.toVector
|
||||||
attackingArmy <- province.hostileArmies
|
attackingArmy <- province.hostileArmies
|
||||||
} yield (defendingFactionId, attackingArmy.factionId)
|
} yield (defendingFactionId, attackingArmy.factionId)
|
||||||
|
|
||||||
private def isFailedTruceQuest(q: QuestT, p: ProvinceT): Boolean =
|
private def isFailedTruceQuest(q: QuestT, p: ProvinceT): Boolean =
|
||||||
q match {
|
q match {
|
||||||
case TruceWithFactionQuest(targetFactionId) =>
|
case TruceWithFactionQuest(targetFactionId) =>
|
||||||
incomingAttacks.exists { case (defendingFid, attackingFid) =>
|
incomingAttacks.exists {
|
||||||
defendingFid == targetFactionId && p.rulingFactionId.contains(
|
case (defendingFid, attackingFid) =>
|
||||||
attackingFid
|
defendingFid == targetFactionId && p.rulingFactionId.contains(
|
||||||
)
|
attackingFid
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
case _ => false
|
case _ => false
|
||||||
|
|||||||
+42
-77
@@ -26,10 +26,7 @@ import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
|||||||
import net.eagle0.eagle.library.actions.applier.ActionResultTApplier
|
import net.eagle0.eagle.library.actions.applier.ActionResultTApplier
|
||||||
import net.eagle0.eagle.library.actions.generated_text_request_generators.captured_hero_helpers.CapturedHeroPleaGenerator
|
import net.eagle0.eagle.library.actions.generated_text_request_generators.captured_hero_helpers.CapturedHeroPleaGenerator
|
||||||
import net.eagle0.eagle.library.actions.impl.action.EndBattleAftermathPhaseAction.RevelationChange
|
import net.eagle0.eagle.library.actions.impl.action.EndBattleAftermathPhaseAction.RevelationChange
|
||||||
import net.eagle0.eagle.library.actions.impl.common.{
|
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, RandomStateTSequencer}
|
||||||
ProtolessRandomSequentialResultsAction,
|
|
||||||
RandomStateTSequencer
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.settings.{
|
import net.eagle0.eagle.library.settings.{
|
||||||
FactionBiasAgainstFormerOnExile,
|
FactionBiasAgainstFormerOnExile,
|
||||||
FactionBiasFromExile,
|
FactionBiasFromExile,
|
||||||
@@ -41,22 +38,10 @@ import net.eagle0.eagle.library.util.unaffiliated_hero.LegacyUnaffiliatedHeroUti
|
|||||||
import net.eagle0.eagle.library.util.view_filters.ProvinceViewFilter
|
import net.eagle0.eagle.library.util.view_filters.ProvinceViewFilter
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||||
import net.eagle0.eagle.library.EagleInternalException
|
import net.eagle0.eagle.library.EagleInternalException
|
||||||
import net.eagle0.eagle.model.action_result.{
|
import net.eagle0.eagle.model.action_result.{ActionResultT, NotificationDetails, NotificationT}
|
||||||
ActionResultT,
|
|
||||||
NotificationDetails,
|
|
||||||
NotificationT
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||||
import net.eagle0.eagle.model.action_result.concrete.{
|
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC, ChangedHeroC, NotificationC}
|
||||||
ActionResultC,
|
import net.eagle0.eagle.model.action_result.types.{CapturedHeroResolvedResultType, EndAftermathPhaseResultType}
|
||||||
ChangedFactionC,
|
|
||||||
ChangedHeroC,
|
|
||||||
NotificationC
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.action_result.types.{
|
|
||||||
CapturedHeroResolvedResultType,
|
|
||||||
EndAftermathPhaseResultType
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.proto_converters.{
|
import net.eagle0.eagle.model.proto_converters.{
|
||||||
BattleRevelationConverter,
|
BattleRevelationConverter,
|
||||||
NotificationConverter,
|
NotificationConverter,
|
||||||
@@ -74,11 +59,7 @@ import net.eagle0.eagle.model.state.hero.{
|
|||||||
CapturedHeroReturnedBackstoryEvent,
|
CapturedHeroReturnedBackstoryEvent,
|
||||||
EventForHeroBackstoryT
|
EventForHeroBackstoryT
|
||||||
}
|
}
|
||||||
import net.eagle0.eagle.model.state.BattleRevelationType.{
|
import net.eagle0.eagle.model.state.BattleRevelationType.{DidBattle, Unknown, Withdrew}
|
||||||
DidBattle,
|
|
||||||
Unknown,
|
|
||||||
Withdrew
|
|
||||||
}
|
|
||||||
|
|
||||||
object EndBattleAftermathPhaseAction {
|
object EndBattleAftermathPhaseAction {
|
||||||
case class RevelationChange(
|
case class RevelationChange(
|
||||||
@@ -112,15 +93,13 @@ object EndBattleAftermathPhaseAction {
|
|||||||
`type` = uhType,
|
`type` = uhType,
|
||||||
lastFaction = gameState.heroes(capturedHeroId).factionId,
|
lastFaction = gameState.heroes(capturedHeroId).factionId,
|
||||||
factionBiases = (
|
factionBiases = (
|
||||||
newFactionBias.map(b => actingFactionId -> b) ++ oldFactionBias.map(
|
newFactionBias.map(b => actingFactionId -> b) ++ oldFactionBias.map(b => capturedHeroFactionId -> b)
|
||||||
b => capturedHeroFactionId -> b
|
|
||||||
)
|
|
||||||
).toMap
|
).toMap
|
||||||
),
|
),
|
||||||
hero = gameState.heroes(capturedHeroId),
|
hero = gameState.heroes(capturedHeroId),
|
||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.map { UnaffiliatedHeroConverter.fromProto }
|
.map(UnaffiliatedHeroConverter.fromProto)
|
||||||
.map { uh =>
|
.map { uh =>
|
||||||
ActionResultC(
|
ActionResultC(
|
||||||
actionResultType = CapturedHeroResolvedResultType,
|
actionResultType = CapturedHeroResolvedResultType,
|
||||||
@@ -129,8 +108,7 @@ object EndBattleAftermathPhaseAction {
|
|||||||
changedHeroes = Vector(
|
changedHeroes = Vector(
|
||||||
ChangedHeroC(
|
ChangedHeroC(
|
||||||
heroId = capturedHeroId,
|
heroId = capturedHeroId,
|
||||||
newEventsForHeroBackstory =
|
newEventsForHeroBackstory = Vector(newEventForHeroBackstoryDetails)
|
||||||
Vector(newEventForHeroBackstoryDetails)
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
changedProvinces = Vector(
|
changedProvinces = Vector(
|
||||||
@@ -274,13 +252,12 @@ object EndBattleAftermathPhaseAction {
|
|||||||
oldFactionBias = None,
|
oldFactionBias = None,
|
||||||
gameState = gameState,
|
gameState = gameState,
|
||||||
functionalRandom = functionalRandom,
|
functionalRandom = functionalRandom,
|
||||||
newEventForHeroBackstoryDetails =
|
newEventForHeroBackstoryDetails = CapturedHeroImprisonedBackstoryEvent(
|
||||||
CapturedHeroImprisonedBackstoryEvent(
|
date = DateConverter.fromProto(gameState.currentDate),
|
||||||
date = DateConverter.fromProto(gameState.currentDate),
|
capturingFactionId = imprisoningFactionId,
|
||||||
capturingFactionId = imprisoningFactionId,
|
capturingHeroId = imprisoningHeroId,
|
||||||
capturingHeroId = imprisoningHeroId,
|
provinceId = provinceId
|
||||||
provinceId = provinceId
|
)
|
||||||
)
|
|
||||||
).map { ar =>
|
).map { ar =>
|
||||||
ar.withNewGeneratedTextRequest(notificationLlmRequest)
|
ar.withNewGeneratedTextRequest(notificationLlmRequest)
|
||||||
.withNotification(
|
.withNotification(
|
||||||
@@ -352,8 +329,7 @@ object EndBattleAftermathPhaseAction {
|
|||||||
factions = allFactionTs
|
factions = allFactionTs
|
||||||
)
|
)
|
||||||
.copy(
|
.copy(
|
||||||
relationshipLevel =
|
relationshipLevel = FactionRelationship.RelationshipLevel.Truce,
|
||||||
FactionRelationship.RelationshipLevel.Truce,
|
|
||||||
resetDate = Some(truceEndDate)
|
resetDate = Some(truceEndDate)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -368,8 +344,7 @@ object EndBattleAftermathPhaseAction {
|
|||||||
factions = allFactionTs
|
factions = allFactionTs
|
||||||
)
|
)
|
||||||
.copy(
|
.copy(
|
||||||
relationshipLevel =
|
relationshipLevel = FactionRelationship.RelationshipLevel.Truce,
|
||||||
FactionRelationship.RelationshipLevel.Truce,
|
|
||||||
resetDate = Some(truceEndDate)
|
resetDate = Some(truceEndDate)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -394,9 +369,8 @@ object EndBattleAftermathPhaseAction {
|
|||||||
throw new EagleInternalException("Empty deferred change")
|
throw new EagleInternalException("Empty deferred change")
|
||||||
|
|
||||||
// the rest are not for this phase
|
// the rest are not for this phase
|
||||||
case _: EpidemicStarted | _: DroughtStarted | _: DroughtEnded |
|
case _: EpidemicStarted | _: DroughtStarted | _: DroughtEnded | _: PrisonerMoved | _: PrisonerReturned |
|
||||||
_: PrisonerMoved | _: PrisonerReturned | _: BlizzardStarted |
|
_: BlizzardStarted | _: BlizzardEnded =>
|
||||||
_: BlizzardEnded =>
|
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
"Event should not be present in EndBattleAftermathPhaseAction"
|
"Event should not be present in EndBattleAftermathPhaseAction"
|
||||||
)
|
)
|
||||||
@@ -412,9 +386,9 @@ case class EndBattleAftermathPhaseAction(
|
|||||||
): RevelationChange =
|
): RevelationChange =
|
||||||
RevelationChange(
|
RevelationChange(
|
||||||
changedFaction = battleRevelation.revelationType match {
|
changedFaction = battleRevelation.revelationType match {
|
||||||
case Unknown =>
|
case Unknown =>
|
||||||
throw new EagleInternalException("UNKNOWN revelation type")
|
throw new EagleInternalException("UNKNOWN revelation type")
|
||||||
case Withdrew =>
|
case Withdrew =>
|
||||||
ChangedFactionC(
|
ChangedFactionC(
|
||||||
factionId = battleRevelation.revealedToFactionId,
|
factionId = battleRevelation.revealedToFactionId,
|
||||||
updatedReconnedProvinces = Vector(
|
updatedReconnedProvinces = Vector(
|
||||||
@@ -450,7 +424,7 @@ case class EndBattleAftermathPhaseAction(
|
|||||||
|
|
||||||
def revelationChanges(gs: GameState): Vector[RevelationChange] =
|
def revelationChanges(gs: GameState): Vector[RevelationChange] =
|
||||||
for {
|
for {
|
||||||
province <- gs.provinces.values.toVector
|
province <- gs.provinces.values.toVector
|
||||||
revelation <- province.battleRevelations
|
revelation <- province.battleRevelations
|
||||||
if gs.factions.contains(revelation.revealedToFactionId)
|
if gs.factions.contains(revelation.revealedToFactionId)
|
||||||
} yield revelationChange(BattleRevelationConverter.fromProto(revelation))
|
} yield revelationChange(BattleRevelationConverter.fromProto(revelation))
|
||||||
@@ -466,9 +440,7 @@ case class EndBattleAftermathPhaseAction(
|
|||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.foldIn(
|
.foldIn(
|
||||||
EndBattleAftermathPhaseAction.allDeferredChanges(gameState =
|
EndBattleAftermathPhaseAction.allDeferredChanges(gameState = initialState)
|
||||||
initialState
|
|
||||||
)
|
|
||||||
)(
|
)(
|
||||||
EndBattleAftermathPhaseAction.deferredChangeAR
|
EndBattleAftermathPhaseAction.deferredChangeAR
|
||||||
)
|
)
|
||||||
@@ -495,37 +467,30 @@ case class EndBattleAftermathPhaseAction(
|
|||||||
.withRandomActionResults((gs, fr) =>
|
.withRandomActionResults((gs, fr) =>
|
||||||
CheckForFactionChangesAction(
|
CheckForFactionChangesAction(
|
||||||
gameId = gs.gameId,
|
gameId = gs.gameId,
|
||||||
factions =
|
factions = gs.factions.values.toVector.map(FactionConverter.fromProto),
|
||||||
gs.factions.values.toVector.map(FactionConverter.fromProto),
|
provinces = gs.provinces.values.toVector.map(ProvinceConverter.fromProto),
|
||||||
provinces =
|
|
||||||
gs.provinces.values.toVector.map(ProvinceConverter.fromProto),
|
|
||||||
heroes = gs.heroes.values.toVector.map(HeroConverter.fromProto),
|
heroes = gs.heroes.values.toVector.map(HeroConverter.fromProto),
|
||||||
killedHeroIds = gs.killedHeroes.keys.toVector
|
killedHeroIds = gs.killedHeroes.keys.toVector
|
||||||
).randomResults(fr)
|
).randomResults(fr)
|
||||||
)
|
)
|
||||||
.withProtolessSequentialResultsAction(gs =>
|
.withProtolessSequentialResultsAction(gs => HeroBackstoryUpdateActionGenerator.fromGameState(gs))
|
||||||
HeroBackstoryUpdateActionGenerator.fromGameState(gs)
|
.withRandomActionResult {
|
||||||
)
|
case (gs, fr) =>
|
||||||
.withRandomActionResult { case (gs, fr) =>
|
RandomState(
|
||||||
RandomState(
|
ActionResultC(
|
||||||
ActionResultC(
|
actionResultType = EndAftermathPhaseResultType,
|
||||||
actionResultType = EndAftermathPhaseResultType,
|
changedFactions = revelationChanges(gs).map(_.changedFaction),
|
||||||
changedFactions = revelationChanges(gs).map(_.changedFaction),
|
changedProvinces = revelationChanges(gs).map(_.changedProvince),
|
||||||
changedProvinces = revelationChanges(gs).map(_.changedProvince),
|
newRoundPhase = Some(RoundPhase.DiplomacyResolution),
|
||||||
newRoundPhase = Some(RoundPhase.DiplomacyResolution),
|
removedNotifications = gs.deferredNotifications
|
||||||
removedNotifications = gs.deferredNotifications
|
.map(note => NotificationConverter.fromProto(note, deferred = true))
|
||||||
.map(note =>
|
.toVector,
|
||||||
NotificationConverter.fromProto(note, deferred = true)
|
newNotifications = gs.deferredNotifications
|
||||||
)
|
.map(note => NotificationConverter.fromProto(note, deferred = false))
|
||||||
.toVector,
|
.toVector
|
||||||
newNotifications = gs.deferredNotifications
|
),
|
||||||
.map(note =>
|
fr
|
||||||
NotificationConverter.fromProto(note, deferred = false)
|
)
|
||||||
)
|
|
||||||
.toVector
|
|
||||||
),
|
|
||||||
fr
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.actionResults
|
.actionResults
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -9,12 +9,12 @@ import net.eagle0.eagle.internal.game_state.GameState
|
|||||||
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||||
|
|
||||||
case class EndBattleRequestPhaseAction(gameState: GameState)
|
case class EndBattleRequestPhaseAction(gameState: GameState) extends DeterministicSingleResultAction(gameState) {
|
||||||
extends DeterministicSingleResultAction(gameState) {
|
|
||||||
override def immediateExecute: ActionResult = {
|
override def immediateExecute: ActionResult = {
|
||||||
internalRequire(
|
internalRequire(
|
||||||
gameState.provinces.forall { case (_, province) =>
|
gameState.provinces.forall {
|
||||||
province.hostileArmies.isEmpty
|
case (_, province) =>
|
||||||
|
province.hostileArmies.isEmpty
|
||||||
},
|
},
|
||||||
s"Province ${gameState.provinces.find(_._2.hostileArmies.nonEmpty).get._2.name} still has attacking armies!"
|
s"Province ${gameState.provinces.find(_._2.hostileArmies.nonEmpty).get._2.name} still has attacking armies!"
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-2
@@ -7,8 +7,7 @@ import net.eagle0.eagle.internal.action_result.ActionResult
|
|||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
||||||
|
|
||||||
case class EndBattleResolutionPhaseAction(gameState: GameState)
|
case class EndBattleResolutionPhaseAction(gameState: GameState) extends DeterministicSingleResultAction(gameState) {
|
||||||
extends DeterministicSingleResultAction(gameState) {
|
|
||||||
override def immediateExecute: ActionResult =
|
override def immediateExecute: ActionResult =
|
||||||
ActionResult(
|
ActionResult(
|
||||||
`type` = END_RESOLUTION_PHASE,
|
`type` = END_RESOLUTION_PHASE,
|
||||||
|
|||||||
+10
-12
@@ -30,21 +30,19 @@ case class EndDefenseDecisionPhaseAction(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val attackerCps = for {
|
val attackerCps = for {
|
||||||
ag <- tributePaidArmies
|
ag <- tributePaidArmies
|
||||||
army <- ag.armies
|
army <- ag.armies
|
||||||
} yield {
|
} yield ChangedProvince(
|
||||||
ChangedProvince(
|
id = army.originProvince,
|
||||||
id = army.originProvince,
|
addedIncomingArmies = Vector(
|
||||||
addedIncomingArmies = Vector(
|
army.update(
|
||||||
army.update(
|
_.destinationProvince := army.originProvince,
|
||||||
_.destinationProvince := army.originProvince,
|
_.originProvince := payingProvince.id,
|
||||||
_.originProvince := payingProvince.id,
|
_.army.optionalFleeProvinceId := None,
|
||||||
_.army.optionalFleeProvinceId := None,
|
_.arrivalRound := gameState.currentRoundId + 1
|
||||||
_.arrivalRound := gameState.currentRoundId + 1
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
|
||||||
if attackerCps.isEmpty then Vector.empty
|
if attackerCps.isEmpty then Vector.empty
|
||||||
else
|
else
|
||||||
|
|||||||
+71
-104
@@ -10,47 +10,23 @@ import net.eagle0.eagle.library.actions.impl.action.diplomacy_helpers.{
|
|||||||
RansomResolutionHelpers,
|
RansomResolutionHelpers,
|
||||||
TruceResolutionHelpers
|
TruceResolutionHelpers
|
||||||
}
|
}
|
||||||
import net.eagle0.eagle.library.actions.impl.common.{
|
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, RandomStateTSequencer}
|
||||||
ProtolessRandomSequentialResultsAction,
|
|
||||||
RandomStateTSequencer
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||||
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentChecker
|
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentChecker
|
||||||
import net.eagle0.eagle.library.util.ransom_validity.RansomValidity
|
import net.eagle0.eagle.library.util.ransom_validity.RansomValidity
|
||||||
import net.eagle0.eagle.library.EagleInternalException
|
import net.eagle0.eagle.library.EagleInternalException
|
||||||
import net.eagle0.eagle.model.action_result.concrete.{
|
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC}
|
||||||
ActionResultC,
|
import net.eagle0.eagle.model.action_result.types.{EndDiplomacyResolutionPhaseResultType, RansomInvalidatedResultType}
|
||||||
ChangedFactionC
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.action_result.types.{
|
|
||||||
EndDiplomacyResolutionPhaseResultType,
|
|
||||||
RansomInvalidatedResultType
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||||
import net.eagle0.eagle.model.proto_converters.{
|
import net.eagle0.eagle.model.proto_converters.{BattalionConverter, NotificationConverter}
|
||||||
BattalionConverter,
|
|
||||||
NotificationConverter
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||||
import net.eagle0.eagle.model.state.date.Date
|
import net.eagle0.eagle.model.state.date.Date
|
||||||
import net.eagle0.eagle.model.state.diplomacy_offer.{
|
import net.eagle0.eagle.model.state.diplomacy_offer.{AllianceOffer, BreakAlliance, Invitation, RansomOffer, TruceOffer}
|
||||||
AllianceOffer,
|
import net.eagle0.eagle.model.state.diplomacy_offer.status.{Accepted, Imprisoned, Invalidated, Rejected, Unresolved}
|
||||||
BreakAlliance,
|
|
||||||
Invitation,
|
|
||||||
RansomOffer,
|
|
||||||
TruceOffer
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{
|
|
||||||
Accepted,
|
|
||||||
Imprisoned,
|
|
||||||
Invalidated,
|
|
||||||
Rejected,
|
|
||||||
Unresolved
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.state.faction.FactionT
|
import net.eagle0.eagle.model.state.faction.FactionT
|
||||||
import net.eagle0.eagle.model.state.hero.HeroT
|
import net.eagle0.eagle.model.state.hero.HeroT
|
||||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||||
@@ -120,58 +96,50 @@ case class EndDiplomacyResolutionPhaseAction(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Companion object for EndDiplomacyResolutionPhaseAction containing helper
|
/**
|
||||||
* methods.
|
* Companion object for EndDiplomacyResolutionPhaseAction containing helper methods.
|
||||||
*
|
*
|
||||||
* IMPORTANT: This is intentionally named differently from the case class to
|
* IMPORTANT: This is intentionally named differently from the case class to avoid Scala naming conflicts that prevent
|
||||||
* avoid Scala naming conflicts that prevent the class from being imported in
|
* the class from being imported in other packages. When a case class and its companion object share the same name, the
|
||||||
* other packages. When a case class and its companion object share the same
|
* compiler can become confused about which one is being referenced during imports.
|
||||||
* name, the compiler can become confused about which one is being referenced
|
*
|
||||||
* during imports.
|
* All helper methods take explicit parameters for game state components to ensure they always operate on the current
|
||||||
*
|
* state as provided by RandomStateTSequencer, preventing use of stale data from the initial game state.
|
||||||
* All helper methods take explicit parameters for game state components to
|
*/
|
||||||
* ensure they always operate on the current state as provided by
|
|
||||||
* RandomStateTSequencer, preventing use of stale data from the initial game
|
|
||||||
* state.
|
|
||||||
*/
|
|
||||||
private object EndDiplomacyResolutionPhaseActionHelpers {
|
private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||||
|
|
||||||
// Methods that use the updated game state from the sequencer
|
// Methods that use the updated game state from the sequencer
|
||||||
def invalidationResultsForState(
|
def invalidationResultsForState(
|
||||||
currentGameState: GameState
|
currentGameState: GameState
|
||||||
): Vector[ActionResultT] = {
|
): Vector[ActionResultT] = {
|
||||||
val factions =
|
val factions =
|
||||||
currentGameState.factions.values.map(FactionConverter.fromProto).toVector
|
currentGameState.factions.values.map(FactionConverter.fromProto).toVector
|
||||||
val provinces = currentGameState.provinces.values
|
val provinces = currentGameState.provinces.values
|
||||||
.map(ProvinceConverter.fromProto)
|
.map(ProvinceConverter.fromProto)
|
||||||
.toVector
|
.toVector
|
||||||
|
|
||||||
for {
|
for {
|
||||||
faction <- factions
|
faction <- factions
|
||||||
diploOffer <- faction.incomingDiplomacyOffers.filter(
|
diploOffer <- faction.incomingDiplomacyOffers.filter(
|
||||||
_.status == Unresolved
|
_.status == Unresolved
|
||||||
)
|
)
|
||||||
} yield {
|
} yield diploOffer match {
|
||||||
diploOffer match {
|
case ro: RansomOffer if !RansomValidity.isStillValid(diploOffer, provinces) =>
|
||||||
case ro: RansomOffer
|
ActionResultC(
|
||||||
if !RansomValidity.isStillValid(diploOffer, provinces) =>
|
actionResultType = RansomInvalidatedResultType,
|
||||||
ActionResultC(
|
changedFactions = Vector(
|
||||||
actionResultType = RansomInvalidatedResultType,
|
ChangedFactionC(
|
||||||
changedFactions = Vector(
|
factionId = faction.id,
|
||||||
ChangedFactionC(
|
removedIncomingDiplomacyOfferFactionIds = Vector(ro.originatingFactionId),
|
||||||
factionId = faction.id,
|
newIncomingDiplomacyOffers = Vector(ro.withStatus(Invalidated))
|
||||||
removedIncomingDiplomacyOfferFactionIds =
|
|
||||||
Vector(ro.originatingFactionId),
|
|
||||||
newIncomingDiplomacyOffers = Vector(ro.withStatus(Invalidated))
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case _ =>
|
)
|
||||||
// This shouldn't be possible
|
case _ =>
|
||||||
throw new EagleInternalException(
|
// This shouldn't be possible
|
||||||
s"Unresolved offer $diploOffer should not still be possible"
|
throw new EagleInternalException(
|
||||||
)
|
s"Unresolved offer $diploOffer should not still be possible"
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +166,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
resolver: A => Vector[ActionResultT]
|
resolver: A => Vector[ActionResultT]
|
||||||
)(factions: Vector[FactionT]): Vector[ActionResultT] =
|
)(factions: Vector[FactionT]): Vector[ActionResultT] =
|
||||||
(for {
|
(for {
|
||||||
faction <- factions
|
faction <- factions
|
||||||
outgoingDiplo <- getter(faction)
|
outgoingDiplo <- getter(faction)
|
||||||
} yield resolver(outgoingDiplo)).flatten
|
} yield resolver(outgoingDiplo)).flatten
|
||||||
|
|
||||||
@@ -207,10 +175,12 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
resolver: (A, FunctionalRandom) => RandomState[Vector[ActionResultT]],
|
resolver: (A, FunctionalRandom) => RandomState[Vector[ActionResultT]],
|
||||||
functionalRandom: FunctionalRandom
|
functionalRandom: FunctionalRandom
|
||||||
)(factions: Vector[FactionT]): RandomState[Vector[ActionResultT]] =
|
)(factions: Vector[FactionT]): RandomState[Vector[ActionResultT]] =
|
||||||
functionalRandom.nextFlatMap(factions) { case (faction, fr) =>
|
functionalRandom.nextFlatMap(factions) {
|
||||||
fr.nextMap(getter(faction)) { case (outgoingDiplo, innerFr) =>
|
case (faction, fr) =>
|
||||||
resolver(outgoingDiplo, innerFr)
|
fr.nextMap(getter(faction)) {
|
||||||
}.map(_.flatten)
|
case (outgoingDiplo, innerFr) =>
|
||||||
|
resolver(outgoingDiplo, innerFr)
|
||||||
|
}.map(_.flatten)
|
||||||
}
|
}
|
||||||
|
|
||||||
def truceResolutionsForState(
|
def truceResolutionsForState(
|
||||||
@@ -221,7 +191,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
randomResolutionsForType(
|
randomResolutionsForType(
|
||||||
_.incomingDiplomacyOffers.collect { case to: TruceOffer => to },
|
_.incomingDiplomacyOffers.collect { case to: TruceOffer => to },
|
||||||
(to: TruceOffer, fr: FunctionalRandom) => {
|
(to: TruceOffer, fr: FunctionalRandom) => {
|
||||||
val provinces = currentGameState.provinces.values
|
val provinces = currentGameState.provinces.values
|
||||||
.map(ProvinceConverter.fromProto)
|
.map(ProvinceConverter.fromProto)
|
||||||
.toVector
|
.toVector
|
||||||
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
||||||
@@ -246,10 +216,10 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
randomResolutionsForType(
|
randomResolutionsForType(
|
||||||
_.incomingDiplomacyOffers.collect { case ao: AllianceOffer => ao },
|
_.incomingDiplomacyOffers.collect { case ao: AllianceOffer => ao },
|
||||||
(ao: AllianceOffer, fr: FunctionalRandom) => {
|
(ao: AllianceOffer, fr: FunctionalRandom) => {
|
||||||
val provinces = currentGameState.provinces.values
|
val provinces = currentGameState.provinces.values
|
||||||
.map(ProvinceConverter.fromProto)
|
.map(ProvinceConverter.fromProto)
|
||||||
.toVector
|
.toVector
|
||||||
val heroes =
|
val heroes =
|
||||||
currentGameState.heroes.values.map(HeroConverter.fromProto).toVector
|
currentGameState.heroes.values.map(HeroConverter.fromProto).toVector
|
||||||
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
||||||
resultsForAllianceOffer(
|
resultsForAllianceOffer(
|
||||||
@@ -273,7 +243,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
randomResolutionsForType(
|
randomResolutionsForType(
|
||||||
_.incomingDiplomacyOffers.collect { case ba: BreakAlliance => ba },
|
_.incomingDiplomacyOffers.collect { case ba: BreakAlliance => ba },
|
||||||
(ba: BreakAlliance, fr: FunctionalRandom) => {
|
(ba: BreakAlliance, fr: FunctionalRandom) => {
|
||||||
val provinces = currentGameState.provinces.values
|
val provinces = currentGameState.provinces.values
|
||||||
.map(ProvinceConverter.fromProto)
|
.map(ProvinceConverter.fromProto)
|
||||||
.toVector
|
.toVector
|
||||||
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
||||||
@@ -291,10 +261,10 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
randomResolutionsForType(
|
randomResolutionsForType(
|
||||||
_.incomingDiplomacyOffers.collect { case id: Invitation => id },
|
_.incomingDiplomacyOffers.collect { case id: Invitation => id },
|
||||||
(id: Invitation, fr: FunctionalRandom) => {
|
(id: Invitation, fr: FunctionalRandom) => {
|
||||||
val provinces = currentGameState.provinces.values
|
val provinces = currentGameState.provinces.values
|
||||||
.map(ProvinceConverter.fromProto)
|
.map(ProvinceConverter.fromProto)
|
||||||
.toVector
|
.toVector
|
||||||
val battalions = currentGameState.battalions.values
|
val battalions = currentGameState.battalions.values
|
||||||
.map(BattalionConverter.fromProto)
|
.map(BattalionConverter.fromProto)
|
||||||
.toVector
|
.toVector
|
||||||
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
||||||
@@ -337,7 +307,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
provinces: Vector[ProvinceT],
|
provinces: Vector[ProvinceT],
|
||||||
gameState: GameState
|
gameState: GameState
|
||||||
): Vector[ActionResultT] = ransomOffer.status match {
|
): Vector[ActionResultT] = ransomOffer.status match {
|
||||||
case Accepted =>
|
case Accepted =>
|
||||||
RansomResolutionHelpers.acceptedRansomResults(
|
RansomResolutionHelpers.acceptedRansomResults(
|
||||||
ransomOffer,
|
ransomOffer,
|
||||||
currentDate,
|
currentDate,
|
||||||
@@ -346,19 +316,18 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
gameId = gameState.gameId,
|
gameId = gameState.gameId,
|
||||||
currentDate = currentDate,
|
currentDate = currentDate,
|
||||||
currentRoundId = gameState.currentRoundId,
|
currentRoundId = gameState.currentRoundId,
|
||||||
previousBackstoryTextIdLookup =
|
previousBackstoryTextIdLookup = hid => gameState.heroes(hid).backstoryVersions.toVector.last.textId
|
||||||
hid => gameState.heroes(hid).backstoryVersions.toVector.last.textId
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case Rejected =>
|
case Rejected =>
|
||||||
Vector(
|
Vector(
|
||||||
RansomResolutionHelpers.rejectedRansomResult(ransomOffer)
|
RansomResolutionHelpers.rejectedRansomResult(ransomOffer)
|
||||||
)
|
)
|
||||||
case Unresolved =>
|
case Unresolved =>
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
"UNRESOLVED ransom offer at the end of the round"
|
"UNRESOLVED ransom offer at the end of the round"
|
||||||
)
|
)
|
||||||
case Imprisoned =>
|
case Imprisoned =>
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
s"IMPRISONED is not valid for a ransom offer"
|
s"IMPRISONED is not valid for a ransom offer"
|
||||||
)
|
)
|
||||||
@@ -377,7 +346,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
gameState: GameState
|
gameState: GameState
|
||||||
): RandomState[Vector[ActionResultT]] =
|
): RandomState[Vector[ActionResultT]] =
|
||||||
truceOffer.status match {
|
truceOffer.status match {
|
||||||
case Accepted =>
|
case Accepted =>
|
||||||
TruceResolutionHelpers
|
TruceResolutionHelpers
|
||||||
.acceptedTruceResult(
|
.acceptedTruceResult(
|
||||||
truceOffer = truceOffer,
|
truceOffer = truceOffer,
|
||||||
@@ -387,7 +356,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.map(Vector(_))
|
.map(Vector(_))
|
||||||
case Rejected =>
|
case Rejected =>
|
||||||
TruceResolutionHelpers
|
TruceResolutionHelpers
|
||||||
.rejectedTruceResults(
|
.rejectedTruceResults(
|
||||||
truceOffer = truceOffer,
|
truceOffer = truceOffer,
|
||||||
@@ -396,7 +365,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
eagleGameId = gameState.gameId,
|
eagleGameId = gameState.gameId,
|
||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
case Imprisoned =>
|
case Imprisoned =>
|
||||||
RandomState(
|
RandomState(
|
||||||
TruceResolutionHelpers.imprisonedAmbassadorResults(
|
TruceResolutionHelpers.imprisonedAmbassadorResults(
|
||||||
truceOffer = truceOffer,
|
truceOffer = truceOffer,
|
||||||
@@ -416,7 +385,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
eagleGameId = gameState.gameId,
|
eagleGameId = gameState.gameId,
|
||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
case Unresolved =>
|
case Unresolved =>
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
"UNRESOLVED truce offer at the end of the round"
|
"UNRESOLVED truce offer at the end of the round"
|
||||||
)
|
)
|
||||||
@@ -429,9 +398,9 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
factions: Vector[FactionT],
|
factions: Vector[FactionT],
|
||||||
heroes: Vector[HeroT],
|
heroes: Vector[HeroT],
|
||||||
currentDate: Date
|
currentDate: Date
|
||||||
): RandomState[Vector[ActionResultT]] = {
|
): RandomState[Vector[ActionResultT]] =
|
||||||
allianceOffer.status match {
|
allianceOffer.status match {
|
||||||
case Accepted =>
|
case Accepted =>
|
||||||
AllianceResolutionHelpers
|
AllianceResolutionHelpers
|
||||||
.acceptedAllianceResult(
|
.acceptedAllianceResult(
|
||||||
allianceOffer = allianceOffer,
|
allianceOffer = allianceOffer,
|
||||||
@@ -441,7 +410,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
factions = factions,
|
factions = factions,
|
||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
case Rejected =>
|
case Rejected =>
|
||||||
AllianceResolutionHelpers
|
AllianceResolutionHelpers
|
||||||
.rejectedAllianceResult(
|
.rejectedAllianceResult(
|
||||||
allianceOffer = allianceOffer,
|
allianceOffer = allianceOffer,
|
||||||
@@ -450,7 +419,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.map(Vector(_))
|
.map(Vector(_))
|
||||||
case Imprisoned =>
|
case Imprisoned =>
|
||||||
RandomState(
|
RandomState(
|
||||||
AllianceResolutionHelpers
|
AllianceResolutionHelpers
|
||||||
.imprisonedAmbassadorResult(
|
.imprisonedAmbassadorResult(
|
||||||
@@ -469,12 +438,11 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.map(Vector(_))
|
.map(Vector(_))
|
||||||
case Unresolved =>
|
case Unresolved =>
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
"UNRESOLVED alliance offer at the end of the round"
|
"UNRESOLVED alliance offer at the end of the round"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private def resultsForBreakAlliance(
|
private def resultsForBreakAlliance(
|
||||||
breakAllianceOffer: BreakAlliance,
|
breakAllianceOffer: BreakAlliance,
|
||||||
@@ -482,9 +450,9 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
provinces: Vector[ProvinceT],
|
provinces: Vector[ProvinceT],
|
||||||
factions: Vector[FactionT],
|
factions: Vector[FactionT],
|
||||||
currentDate: Date
|
currentDate: Date
|
||||||
): RandomState[Vector[ActionResultT]] = {
|
): RandomState[Vector[ActionResultT]] =
|
||||||
breakAllianceOffer.status match {
|
breakAllianceOffer.status match {
|
||||||
case Accepted =>
|
case Accepted =>
|
||||||
BreakAllianceResolutionHelpers
|
BreakAllianceResolutionHelpers
|
||||||
.acceptedResult(
|
.acceptedResult(
|
||||||
breakAllianceOffer = breakAllianceOffer,
|
breakAllianceOffer = breakAllianceOffer,
|
||||||
@@ -494,7 +462,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.map(Vector(_))
|
.map(Vector(_))
|
||||||
case Rejected =>
|
case Rejected =>
|
||||||
// This one shouldn't be possible, but we'll resolve in the helper
|
// This one shouldn't be possible, but we'll resolve in the helper
|
||||||
BreakAllianceResolutionHelpers
|
BreakAllianceResolutionHelpers
|
||||||
.rejectedResult(
|
.rejectedResult(
|
||||||
@@ -502,7 +470,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.map(Vector(_))
|
.map(Vector(_))
|
||||||
case Imprisoned =>
|
case Imprisoned =>
|
||||||
RandomState(
|
RandomState(
|
||||||
BreakAllianceResolutionHelpers
|
BreakAllianceResolutionHelpers
|
||||||
.imprisonedAmbassadorResult(
|
.imprisonedAmbassadorResult(
|
||||||
@@ -513,7 +481,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
),
|
),
|
||||||
functionalRandom
|
functionalRandom
|
||||||
).map(Vector(_))
|
).map(Vector(_))
|
||||||
case Unresolved =>
|
case Unresolved =>
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
"UNRESOLVED break alliance at the end of the round"
|
"UNRESOLVED break alliance at the end of the round"
|
||||||
)
|
)
|
||||||
@@ -526,7 +494,6 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
)
|
)
|
||||||
.map(Vector(_))
|
.map(Vector(_))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private def resultsForInvitation(
|
private def resultsForInvitation(
|
||||||
invitation: Invitation,
|
invitation: Invitation,
|
||||||
@@ -536,7 +503,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
battalions: Vector[BattalionT],
|
battalions: Vector[BattalionT],
|
||||||
currentDate: Date
|
currentDate: Date
|
||||||
): RandomState[Vector[ActionResultT]] = invitation.status match {
|
): RandomState[Vector[ActionResultT]] = invitation.status match {
|
||||||
case Accepted =>
|
case Accepted =>
|
||||||
InvitationResolutionHelpers.acceptedInvitationResults(
|
InvitationResolutionHelpers.acceptedInvitationResults(
|
||||||
acceptedInvitation = invitation,
|
acceptedInvitation = invitation,
|
||||||
currentDate = currentDate,
|
currentDate = currentDate,
|
||||||
@@ -545,7 +512,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
battalions = battalions,
|
battalions = battalions,
|
||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
case Rejected =>
|
case Rejected =>
|
||||||
InvitationResolutionHelpers
|
InvitationResolutionHelpers
|
||||||
.rejectedInvitationResult(
|
.rejectedInvitationResult(
|
||||||
rejectedInvitation = invitation,
|
rejectedInvitation = invitation,
|
||||||
@@ -554,7 +521,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
functionalRandom = functionalRandom
|
functionalRandom = functionalRandom
|
||||||
)
|
)
|
||||||
.map(Vector(_))
|
.map(Vector(_))
|
||||||
case Imprisoned =>
|
case Imprisoned =>
|
||||||
RandomState(
|
RandomState(
|
||||||
Vector(
|
Vector(
|
||||||
InvitationResolutionHelpers.imprisonedAmbassadorResult(
|
InvitationResolutionHelpers.imprisonedAmbassadorResult(
|
||||||
@@ -566,7 +533,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
|||||||
),
|
),
|
||||||
functionalRandom
|
functionalRandom
|
||||||
)
|
)
|
||||||
case Unresolved =>
|
case Unresolved =>
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
"UNRESOLVED invitation at the end of the round"
|
"UNRESOLVED invitation at the end of the round"
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-2
@@ -12,7 +12,6 @@ case class EndFreeForAllBattleRequestPhaseAction(gameState: GameState)
|
|||||||
override def immediateExecute: ActionResult =
|
override def immediateExecute: ActionResult =
|
||||||
ActionResult(
|
ActionResult(
|
||||||
`type` = END_FREE_FOR_ALL_BATTLE_REQUEST_PHASE,
|
`type` = END_FREE_FOR_ALL_BATTLE_REQUEST_PHASE,
|
||||||
newRoundPhase =
|
newRoundPhase = Some(NewRoundPhase(value = FREE_FOR_ALL_BATTLE_RESOLUTION))
|
||||||
Some(NewRoundPhase(value = FREE_FOR_ALL_BATTLE_RESOLUTION))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -8,8 +8,7 @@ import net.eagle0.eagle.model.action_result.ActionResultT
|
|||||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||||
import net.eagle0.eagle.model.state.RoundPhase
|
import net.eagle0.eagle.model.state.RoundPhase
|
||||||
|
|
||||||
case class EndFreeForAllDecisionPhaseAction(gameState: GameState)
|
case class EndFreeForAllDecisionPhaseAction(gameState: GameState) extends ProtolessSequentialResultsAction {
|
||||||
extends ProtolessSequentialResultsAction {
|
|
||||||
|
|
||||||
override def results: Vector[ActionResultT] =
|
override def results: Vector[ActionResultT] =
|
||||||
WithdrawnArmiesReturnHomeAction(
|
WithdrawnArmiesReturnHomeAction(
|
||||||
|
|||||||
+36
-39
@@ -9,14 +9,8 @@ import net.eagle0.eagle.internal.action_result.ActionResult
|
|||||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
||||||
import net.eagle0.eagle.library.actions.impl.command.{
|
import net.eagle0.eagle.library.actions.impl.command.{CommandFactory, LegacyHandleRiotUtils}
|
||||||
CommandFactory,
|
import net.eagle0.eagle.library.actions.impl.common.{RandomSequentialResultsAction, RandomStateProtoSequencer}
|
||||||
LegacyHandleRiotUtils
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.actions.impl.common.{
|
|
||||||
RandomSequentialResultsAction,
|
|
||||||
RandomStateProtoSequencer
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChoiceHelpers
|
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChoiceHelpers
|
||||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||||
@@ -34,29 +28,31 @@ case class EndHandleRiotsPhaseAction(
|
|||||||
ars.lastStateProto.provinces.values
|
ars.lastStateProto.provinces.values
|
||||||
.filter(LegacyProvinceUtils.hasImminentRiot)
|
.filter(LegacyProvinceUtils.hasImminentRiot)
|
||||||
.filterNot(_.hasActed)
|
.filterNot(_.hasActed)
|
||||||
.foldLeft(ars) { case (sequencer, p) =>
|
.foldLeft(ars) {
|
||||||
commandsForProvince(p.id).map { opac =>
|
case (sequencer, p) =>
|
||||||
sequencer.withRandomAction { case (gs, fr) =>
|
commandsForProvince(p.id).map { opac =>
|
||||||
CommandChoiceHelpers
|
sequencer.withRandomAction {
|
||||||
.handleRiotSelectedCommand(
|
case (gs, fr) =>
|
||||||
actingFactionId = p.getRulingFactionId,
|
CommandChoiceHelpers
|
||||||
gameState = gs,
|
.handleRiotSelectedCommand(
|
||||||
availableCommands = opac.commands.toVector,
|
actingFactionId = p.getRulingFactionId,
|
||||||
functionalRandom = fr
|
gameState = gs,
|
||||||
)
|
availableCommands = opac.commands.toVector,
|
||||||
.map { optCS =>
|
functionalRandom = fr
|
||||||
optCS.map { cs =>
|
)
|
||||||
commandFactory
|
.map { optCS =>
|
||||||
.makeCommand(
|
optCS.map { cs =>
|
||||||
actingFactionId = p.getRulingFactionId,
|
commandFactory
|
||||||
gameState = GameStateConverter.fromProto(gs),
|
.makeCommand(
|
||||||
availableCommand = cs.available,
|
actingFactionId = p.getRulingFactionId,
|
||||||
selectedCommand = cs.selected
|
gameState = GameStateConverter.fromProto(gs),
|
||||||
)
|
availableCommand = cs.available,
|
||||||
}.get
|
selectedCommand = cs.selected
|
||||||
}
|
)
|
||||||
}
|
}.get
|
||||||
}.get
|
}
|
||||||
|
}
|
||||||
|
}.get
|
||||||
}
|
}
|
||||||
|
|
||||||
private def riotOccurredResults(
|
private def riotOccurredResults(
|
||||||
@@ -64,14 +60,15 @@ case class EndHandleRiotsPhaseAction(
|
|||||||
): RandomStateProtoSequencer =
|
): RandomStateProtoSequencer =
|
||||||
ars.lastStateProto.provinces.values
|
ars.lastStateProto.provinces.values
|
||||||
.filter(LegacyProvinceUtils.hasImminentRiot)
|
.filter(LegacyProvinceUtils.hasImminentRiot)
|
||||||
.foldLeft(ars) { case (ars, p) =>
|
.foldLeft(ars) {
|
||||||
ars.withActionResult(_ =>
|
case (ars, p) =>
|
||||||
LegacyHandleRiotUtils
|
ars.withActionResult(_ =>
|
||||||
.riotOccurredAr(
|
LegacyHandleRiotUtils
|
||||||
p.getRulingFactionId,
|
.riotOccurredAr(
|
||||||
ProvinceConverter.fromProto(p)
|
p.getRulingFactionId,
|
||||||
)
|
ProvinceConverter.fromProto(p)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def endPhaseResult(
|
def endPhaseResult(
|
||||||
|
|||||||
+23
-44
@@ -17,11 +17,7 @@ import net.eagle0.eagle.library.util.ProvinceEventUtils
|
|||||||
import net.eagle0.eagle.library.EagleInternalException
|
import net.eagle0.eagle.library.EagleInternalException
|
||||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||||
import net.eagle0.eagle.model.action_result.changed_province.ChangedProvinceT
|
import net.eagle0.eagle.model.action_result.changed_province.ChangedProvinceT
|
||||||
import net.eagle0.eagle.model.action_result.concrete.{
|
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC, ChangedHeroC}
|
||||||
ActionResultC,
|
|
||||||
ChangedFactionC,
|
|
||||||
ChangedHeroC
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.action_result.types.{
|
import net.eagle0.eagle.model.action_result.types.{
|
||||||
EndPlayerCommandsPhaseResultType,
|
EndPlayerCommandsPhaseResultType,
|
||||||
EpidemicTookEffectResultType,
|
EpidemicTookEffectResultType,
|
||||||
@@ -30,27 +26,14 @@ import net.eagle0.eagle.model.action_result.types.{
|
|||||||
WeatherTookEffectResultType
|
WeatherTookEffectResultType
|
||||||
}
|
}
|
||||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||||
import net.eagle0.eagle.model.proto_converters.{
|
import net.eagle0.eagle.model.proto_converters.{NotificationConverter, UnaffiliatedHeroConverter}
|
||||||
NotificationConverter,
|
|
||||||
UnaffiliatedHeroConverter
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.province.{
|
import net.eagle0.eagle.model.proto_converters.province.{ProvinceConverter, ProvinceEventConverter}
|
||||||
ProvinceConverter,
|
import net.eagle0.eagle.model.state.province.{BlizzardEvent, DroughtEvent, EpidemicEvent}
|
||||||
ProvinceEventConverter
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.state.province.{
|
|
||||||
BlizzardEvent,
|
|
||||||
DroughtEvent,
|
|
||||||
EpidemicEvent
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.state.unaffiliated_hero.RecruitmentInfo
|
import net.eagle0.eagle.model.state.unaffiliated_hero.RecruitmentInfo
|
||||||
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{
|
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{Outlaw, Prisoner}
|
||||||
Outlaw,
|
|
||||||
Prisoner
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.state.RoundPhase
|
import net.eagle0.eagle.model.state.RoundPhase
|
||||||
import net.eagle0.eagle.ProvinceId
|
import net.eagle0.eagle.ProvinceId
|
||||||
|
|
||||||
@@ -148,8 +131,7 @@ case class EndPlayerCommandsPhaseAction(
|
|||||||
actionResultType = PrisonerMoveTookEffectResultType,
|
actionResultType = PrisonerMoveTookEffectResultType,
|
||||||
provinceId = Some(pid),
|
provinceId = Some(pid),
|
||||||
actingFactionId = Some(prisonerReturned.fromFactionId),
|
actingFactionId = Some(prisonerReturned.fromFactionId),
|
||||||
affectedFactionIds =
|
affectedFactionIds = Vector(prisonerReturned.fromFactionId, prisonerReturned.toFactionId),
|
||||||
Vector(prisonerReturned.fromFactionId, prisonerReturned.toFactionId),
|
|
||||||
changedHeroes = Vector(
|
changedHeroes = Vector(
|
||||||
ChangedHeroC(
|
ChangedHeroC(
|
||||||
heroId = prisonerReturned.heroId,
|
heroId = prisonerReturned.heroId,
|
||||||
@@ -181,13 +163,12 @@ case class EndPlayerCommandsPhaseAction(
|
|||||||
VigorXPApplier.withVigorXp(
|
VigorXPApplier.withVigorXp(
|
||||||
ActionResultC(
|
ActionResultC(
|
||||||
actionResultType = deferredChange match {
|
actionResultType = deferredChange match {
|
||||||
case _: EpidemicStarted => EpidemicTookEffectResultType
|
case _: EpidemicStarted => EpidemicTookEffectResultType
|
||||||
case _: BlizzardEnded => WeatherTookEffectResultType
|
case _: BlizzardEnded => WeatherTookEffectResultType
|
||||||
case _: BlizzardStarted => WeatherTookEffectResultType
|
case _: BlizzardStarted => WeatherTookEffectResultType
|
||||||
case _: DroughtStarted => WeatherTookEffectResultType
|
case _: DroughtStarted => WeatherTookEffectResultType
|
||||||
case _: DroughtEnded => WeatherTookEffectResultType
|
case _: DroughtEnded => WeatherTookEffectResultType
|
||||||
case _: PrisonerMoved | _: PrisonerReturned |
|
case _: PrisonerMoved | _: PrisonerReturned | _: CapturedHeroImprisoned | _: CapturedHeroExecuted |
|
||||||
_: CapturedHeroImprisoned | _: CapturedHeroExecuted |
|
|
||||||
_: CapturedHeroExiled | _: CapturedHeroReturned =>
|
_: CapturedHeroExiled | _: CapturedHeroReturned =>
|
||||||
throw new EagleInternalException(
|
throw new EagleInternalException(
|
||||||
"Prisoner management changes should not be here"
|
"Prisoner management changes should not be here"
|
||||||
@@ -284,11 +265,11 @@ case class EndPlayerCommandsPhaseAction(
|
|||||||
fr: FunctionalRandom
|
fr: FunctionalRandom
|
||||||
): RandomState[ActionResultT] =
|
): RandomState[ActionResultT] =
|
||||||
deferredChange match {
|
deferredChange match {
|
||||||
case prisonerMoved: PrisonerMoved =>
|
case prisonerMoved: PrisonerMoved =>
|
||||||
onePrisonerMovedChange(pid, prisonerMoved, gs, fr)
|
onePrisonerMovedChange(pid, prisonerMoved, gs, fr)
|
||||||
case prisonerReturned: PrisonerReturned =>
|
case prisonerReturned: PrisonerReturned =>
|
||||||
onePrisonerReturnedChange(pid, prisonerReturned, gs, fr)
|
onePrisonerReturnedChange(pid, prisonerReturned, gs, fr)
|
||||||
case _ => notPrisonerChange(pid, deferredChange, gs, fr)
|
case _ => notPrisonerChange(pid, deferredChange, gs, fr)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def deferredProvinceChangesResultsForProvince(
|
private def deferredProvinceChangesResultsForProvince(
|
||||||
@@ -297,8 +278,9 @@ case class EndPlayerCommandsPhaseAction(
|
|||||||
): RandomStateTSequencer =
|
): RandomStateTSequencer =
|
||||||
arsRS.lastStateProto.provinces(pid).deferredChanges.foldLeft(arsRS) {
|
arsRS.lastStateProto.provinces(pid).deferredChanges.foldLeft(arsRS) {
|
||||||
case (acc, dc) =>
|
case (acc, dc) =>
|
||||||
acc.withRandomActionResult { case (gs, fr) =>
|
acc.withRandomActionResult {
|
||||||
oneDeferredProvinceChange(pid, dc, gs, fr)
|
case (gs, fr) =>
|
||||||
|
oneDeferredProvinceChange(pid, dc, gs, fr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,8 +288,9 @@ case class EndPlayerCommandsPhaseAction(
|
|||||||
ars: RandomStateTSequencer
|
ars: RandomStateTSequencer
|
||||||
): RandomStateTSequencer =
|
): RandomStateTSequencer =
|
||||||
ars.lastStateProto.provinces.keys
|
ars.lastStateProto.provinces.keys
|
||||||
.foldLeft(ars) { case (newArs, pid) =>
|
.foldLeft(ars) {
|
||||||
deferredProvinceChangesResultsForProvince(pid, newArs)
|
case (newArs, pid) =>
|
||||||
|
deferredProvinceChangesResultsForProvince(pid, newArs)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def randomResults(
|
override def randomResults(
|
||||||
@@ -328,18 +311,14 @@ case class EndPlayerCommandsPhaseAction(
|
|||||||
.withRandomActionResults((gs, fr) =>
|
.withRandomActionResults((gs, fr) =>
|
||||||
CheckForFactionChangesAction(
|
CheckForFactionChangesAction(
|
||||||
gameId = gs.gameId,
|
gameId = gs.gameId,
|
||||||
factions =
|
factions = gs.factions.values.toVector.map(FactionConverter.fromProto),
|
||||||
gs.factions.values.toVector.map(FactionConverter.fromProto),
|
provinces = gs.provinces.values.toVector.map(ProvinceConverter.fromProto),
|
||||||
provinces =
|
|
||||||
gs.provinces.values.toVector.map(ProvinceConverter.fromProto),
|
|
||||||
heroes = gs.heroes.values.toVector.map(HeroConverter.fromProto),
|
heroes = gs.heroes.values.toVector.map(HeroConverter.fromProto),
|
||||||
killedHeroIds = gs.killedHeroes.keys.toVector
|
killedHeroIds = gs.killedHeroes.keys.toVector
|
||||||
).randomResults(fr)
|
).randomResults(fr)
|
||||||
)
|
)
|
||||||
.withContinuance(deferredProvinceChangesResults)
|
.withContinuance(deferredProvinceChangesResults)
|
||||||
.withProtolessSequentialResultsAction(gs =>
|
.withProtolessSequentialResultsAction(gs => HeroBackstoryUpdateActionGenerator.fromGameState(gs))
|
||||||
HeroBackstoryUpdateActionGenerator.fromGameState(gs)
|
|
||||||
)
|
|
||||||
.withActionResult(endPhaseResult)
|
.withActionResult(endPhaseResult)
|
||||||
.actionResults
|
.actionResults
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -7,8 +7,7 @@ import net.eagle0.eagle.internal.action_result.ActionResult
|
|||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
||||||
|
|
||||||
case class EndPleaseRecruitMePhaseAction(gameState: GameState)
|
case class EndPleaseRecruitMePhaseAction(gameState: GameState) extends DeterministicSingleResultAction(gameState) {
|
||||||
extends DeterministicSingleResultAction(gameState) {
|
|
||||||
override def immediateExecute: ActionResult =
|
override def immediateExecute: ActionResult =
|
||||||
ActionResult(
|
ActionResult(
|
||||||
`type` = END_PLEASE_RECRUIT_ME_PHASE,
|
`type` = END_PLEASE_RECRUIT_ME_PHASE,
|
||||||
|
|||||||
+33
-38
@@ -28,8 +28,8 @@ case class EndUnaffiliatedHeroActionsPhaseAction(
|
|||||||
private def heroRejoinResults: Vector[ActionResultT] =
|
private def heroRejoinResults: Vector[ActionResultT] =
|
||||||
(for {
|
(for {
|
||||||
province <- provinces.filter(_.rulingFactionId.isDefined)
|
province <- provinces.filter(_.rulingFactionId.isDefined)
|
||||||
uh <- province.unaffiliatedHeroes
|
uh <- province.unaffiliatedHeroes
|
||||||
.filter(_.unaffiliatedHeroType == Outlaw)
|
.filter(_.unaffiliatedHeroType == Outlaw)
|
||||||
} yield UnaffiliatedHeroRejoinedAction.outlawRejoinResult(
|
} yield UnaffiliatedHeroRejoinedAction.outlawRejoinResult(
|
||||||
uh,
|
uh,
|
||||||
province.id,
|
province.id,
|
||||||
@@ -38,45 +38,40 @@ case class EndUnaffiliatedHeroActionsPhaseAction(
|
|||||||
|
|
||||||
private def oneProvincePleaseRecruitMeRequests(
|
private def oneProvincePleaseRecruitMeRequests(
|
||||||
province: ProvinceT
|
province: ProvinceT
|
||||||
): Vector[(LlmRequestT, UnaffiliatedHeroT)] = {
|
): Vector[(LlmRequestT, UnaffiliatedHeroT)] =
|
||||||
province.rulingFactionId
|
province.rulingFactionId.map { fid =>
|
||||||
.map { fid =>
|
val actingFaction = factions.find(_.id == fid).get
|
||||||
val actingFaction = factions.find(_.id == fid).get
|
province.unaffiliatedHeroes
|
||||||
province.unaffiliatedHeroes
|
// Filter out outlaws that are going to rejoin the faction anyway
|
||||||
// Filter out outlaws that are going to rejoin the faction anyway
|
.filterNot(uh => uh.unaffiliatedHeroType == Outlaw && uh.lastFactionId == province.rulingFactionId)
|
||||||
.filterNot(uh =>
|
.filter(uh =>
|
||||||
uh.unaffiliatedHeroType == Outlaw && uh.lastFactionId == province.rulingFactionId
|
UnaffiliatedHeroUtils.willPleaseRecruitMe(
|
||||||
|
targetHero = heroes.find(_.id == uh.heroId).get,
|
||||||
|
unaffiliatedHero = uh,
|
||||||
|
actingFaction = actingFaction,
|
||||||
|
actingFactionLeader = heroes.find(_.id == actingFaction.factionHeadId).get,
|
||||||
|
allProvinces = provinces,
|
||||||
|
allFactions = factions,
|
||||||
|
currentDate = currentDate
|
||||||
)
|
)
|
||||||
.filter(uh =>
|
)
|
||||||
UnaffiliatedHeroUtils.willPleaseRecruitMe(
|
.map { uh =>
|
||||||
targetHero = heroes.find(_.id == uh.heroId).get,
|
val textId = s"please recruit me $currentRoundId ${uh.heroId}"
|
||||||
unaffiliatedHero = uh,
|
(
|
||||||
actingFaction = actingFaction,
|
PleaseRecruitMeMessage(
|
||||||
actingFactionLeader =
|
requestId = textId,
|
||||||
heroes.find(_.id == actingFaction.factionHeadId).get,
|
eagleGameId = gameId,
|
||||||
allProvinces = provinces,
|
recipientFactionIds = Vector(fid),
|
||||||
allFactions = factions,
|
alwaysGenerate = false,
|
||||||
currentDate = currentDate
|
heroId = uh.heroId,
|
||||||
)
|
targetFactionId = fid,
|
||||||
|
provinceId = province.id
|
||||||
|
),
|
||||||
|
uh.withPleaseRecruitMeTextId(textId)
|
||||||
)
|
)
|
||||||
.map { uh =>
|
}
|
||||||
val textId = s"please recruit me $currentRoundId ${uh.heroId}"
|
}
|
||||||
(
|
|
||||||
PleaseRecruitMeMessage(
|
|
||||||
requestId = textId,
|
|
||||||
eagleGameId = gameId,
|
|
||||||
recipientFactionIds = Vector(fid),
|
|
||||||
alwaysGenerate = false,
|
|
||||||
heroId = uh.heroId,
|
|
||||||
targetFactionId = fid,
|
|
||||||
provinceId = province.id
|
|
||||||
),
|
|
||||||
uh.withPleaseRecruitMeTextId(textId)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.getOrElse(Vector())
|
.getOrElse(Vector())
|
||||||
}
|
|
||||||
|
|
||||||
override def results: Vector[ActionResultT] = {
|
override def results: Vector[ActionResultT] = {
|
||||||
val cpsAndLlmRequests = provinces.map { p =>
|
val cpsAndLlmRequests = provinces.map { p =>
|
||||||
|
|||||||
+10
-25
@@ -7,22 +7,15 @@ import net.eagle0.eagle.common.round_phase.RoundPhase.PLAYER_COMMANDS
|
|||||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||||
import net.eagle0.eagle.internal.game_state.GameState
|
import net.eagle0.eagle.internal.game_state.GameState
|
||||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
||||||
import net.eagle0.eagle.library.actions.impl.common.{
|
import net.eagle0.eagle.library.actions.impl.common.{RandomSequentialResultsAction, RandomStateProtoSequencer}
|
||||||
RandomSequentialResultsAction,
|
|
||||||
RandomStateProtoSequencer
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||||
import net.eagle0.eagle.model.proto_converters.{
|
import net.eagle0.eagle.model.proto_converters.{ActionResultProtoConverter, BattalionConverter}
|
||||||
ActionResultProtoConverter,
|
|
||||||
BattalionConverter
|
|
||||||
}
|
|
||||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||||
|
|
||||||
case class EndVassalCommandsPhaseAction(gameState: GameState)
|
case class EndVassalCommandsPhaseAction(gameState: GameState) extends RandomSequentialResultsAction(gameState) {
|
||||||
extends RandomSequentialResultsAction(gameState) {
|
|
||||||
override def randomResults(
|
override def randomResults(
|
||||||
functionalRandom: FunctionalRandom,
|
functionalRandom: FunctionalRandom,
|
||||||
actionResultProtoApplier: ActionResultProtoApplier
|
actionResultProtoApplier: ActionResultProtoApplier
|
||||||
@@ -44,15 +37,12 @@ case class EndVassalCommandsPhaseAction(gameState: GameState)
|
|||||||
gameId = gs.gameId,
|
gameId = gs.gameId,
|
||||||
currentDate = DateConverter.fromProto(gs.currentDate),
|
currentDate = DateConverter.fromProto(gs.currentDate),
|
||||||
currentRoundId = gs.currentRoundId,
|
currentRoundId = gs.currentRoundId,
|
||||||
provinces =
|
provinces = gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||||
gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
|
||||||
factions = gs.factions.values
|
factions = gs.factions.values
|
||||||
.map(FactionConverter.fromProto)
|
.map(FactionConverter.fromProto)
|
||||||
.toVector,
|
.toVector,
|
||||||
battalions =
|
battalions = gs.battalions.values.toVector.map(BattalionConverter.fromProto),
|
||||||
gs.battalions.values.toVector.map(BattalionConverter.fromProto),
|
getHero = hid => gameState.heroes.get(hid).map(HeroConverter.fromProto),
|
||||||
getHero =
|
|
||||||
hid => gameState.heroes.get(hid).map(HeroConverter.fromProto),
|
|
||||||
battalionTypes = gs.battalionTypes.toVector,
|
battalionTypes = gs.battalionTypes.toVector,
|
||||||
hid => gs.heroes(hid).backstoryVersions.last.textId
|
hid => gs.heroes(hid).backstoryVersions.last.textId
|
||||||
)
|
)
|
||||||
@@ -62,8 +52,7 @@ case class EndVassalCommandsPhaseAction(gameState: GameState)
|
|||||||
gameId = gs.gameId,
|
gameId = gs.gameId,
|
||||||
currentDate = DateConverter.fromProto(gs.currentDate),
|
currentDate = DateConverter.fromProto(gs.currentDate),
|
||||||
currentRoundId = gs.currentRoundId,
|
currentRoundId = gs.currentRoundId,
|
||||||
provinces =
|
provinces = gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||||
gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
|
||||||
factions = gs.factions.values
|
factions = gs.factions.values
|
||||||
.map(FactionConverter.fromProto)
|
.map(FactionConverter.fromProto)
|
||||||
.toVector,
|
.toVector,
|
||||||
@@ -73,17 +62,13 @@ case class EndVassalCommandsPhaseAction(gameState: GameState)
|
|||||||
.withRandomActionResults((gs, fr) =>
|
.withRandomActionResults((gs, fr) =>
|
||||||
CheckForFactionChangesAction(
|
CheckForFactionChangesAction(
|
||||||
gameId = gs.gameId,
|
gameId = gs.gameId,
|
||||||
factions =
|
factions = gs.factions.values.map(FactionConverter.fromProto).toVector,
|
||||||
gs.factions.values.map(FactionConverter.fromProto).toVector,
|
provinces = gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||||
provinces =
|
|
||||||
gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
|
||||||
heroes = gs.heroes.values.map(HeroConverter.fromProto).toVector,
|
heroes = gs.heroes.values.map(HeroConverter.fromProto).toVector,
|
||||||
killedHeroIds = gs.killedHeroes.keys.toVector
|
killedHeroIds = gs.killedHeroes.keys.toVector
|
||||||
).randomResults(fr).map(_.map(ActionResultProtoConverter.toProto))
|
).randomResults(fr).map(_.map(ActionResultProtoConverter.toProto))
|
||||||
)
|
)
|
||||||
.withProtolessSequentialResultsAction(gs =>
|
.withProtolessSequentialResultsAction(gs => HeroBackstoryUpdateActionGenerator.fromGameState(gs))
|
||||||
HeroBackstoryUpdateActionGenerator.fromGameState(gs)
|
|
||||||
)
|
|
||||||
.withActionWithResultingState(gs =>
|
.withActionWithResultingState(gs =>
|
||||||
actionResultProtoApplier.applyActionResult(
|
actionResultProtoApplier.applyActionResult(
|
||||||
gs,
|
gs,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user