AI 구현중

This commit is contained in:
2020-04-10 03:19:56 +09:00
parent d0e20eb8d3
commit 33014dbd7d
6 changed files with 279 additions and 204 deletions
+24 -12
View File
@@ -1,6 +1,9 @@
<?php
namespace sammo;
use sammo\Command\GeneralCommand;
use sammo\Command\NationCommand;
function getGeneralTurnBrief(General $generalObj, array $turnList) {
$result = [];
@@ -164,29 +167,40 @@ function pullNationCommand(int $nationID, int $level, int $turnCnt=1){
], 'nation_id=%i AND level=%i ORDER BY turn_idx ASC', $nationID, $level);
}
function _setGeneralCommand(int $generalID, array $turnList, string $command, ?array $arg, string $brief) {
function _setGeneralCommand(GeneralCommand $command, array $turnList):void {
if(!$turnList){
return;
}
$db = DB::db();
$generalID = $command->getGeneral()->getID();
$commandName = $command->getRawClassName();
$arg = $command->getArg();
$brief = $command->getBrief();
$db->update('general_turn', [
'action'=>$command,
'action'=>$commandName,
'arg'=>Json::encode($arg, JSON::EMPTY_ARRAY_IS_DICT),
'brief'=>$brief
], 'general_id = %i AND turn_idx IN %li', $generalID, $turnList);
}
function _setNationCommand(int $nationID, int $level, array $turnList, string $command, ?array $arg, string $brief) {
function _setNationCommand(NationCommand $command, array $turnList):void {
if(!$turnList){
return;
}
$db = DB::db();
$nationID = $command->getNationID();
$level = $command->getGeneralLevel();
$commandName = $command->getRawClassName();
$arg = $command->getArg();
$brief = $command->getBrief();
$db->update('nation_turn', [
'action'=>$command,
'action'=>$commandName,
'arg'=>Json::encode($arg, JSON::EMPTY_ARRAY_IS_DICT),
'brief'=>$brief
], 'nation_id = %i AND level = %i AND turn_idx IN %li', $nationID, $level, $turnList);
@@ -313,18 +327,16 @@ function setGeneralCommand(int $generalID, array $rawTurnList, string $command,
];
}
/*if(!$commandObj->isReservable()){
if(!$commandObj->isReservable()){
return [
'result'=>false,
'reason'=>'예약 불가능한 커맨드 :'.$commandObj->testReservable(),
'test'=>'isReservable',
'target'=>'command'
];
}*/
}
$brief = $commandObj->getBrief();
_setGeneralCommand($generalID, $turnList, $command, $arg, $brief);
_setGeneralCommand($commandObj, $turnList);
return [
'result'=>true,
'reason'=>'success'
@@ -395,20 +407,20 @@ function setNationCommand(int $generalID, array $turnList, string $command, ?arr
];
}
/*if(!$commandObj->isReservable()){
if(!$commandObj->isReservable()){
return [
'result'=>false,
'reason'=>'예약 불가능한 커맨드 :'.$commandObj->testReservable(),
'test'=>'isReservable',
'target'=>'command'
];
}*/
}
//TODO: Reservable은 '정말로 입력 불가'이고, '입력은 가능하지만 실행은 안될 것 같은' 군을 하나더 추가해야함
// Runnable은 Arg를 모두 받아서 처리해야 하는 것이고, Arg를 받지 않아도 안될 것 같지만 입력 자체는 가능할 것 같은 커맨드.
$brief = $commandObj->getBrief();
_setNationCommand($general->getNationID(), $general->getVar('level'), $turnList, $command, $arg, $brief);
_setNationCommand($commandObj, $turnList);
return [
'result'=>true,
'arg_test'=>true,
+16 -4
View File
@@ -229,11 +229,23 @@ function updateQuaterly() {
$connect=$db->get();
//천도 제한 해제, 관직 변경 제한 해제
$query = "update nation set capset='0',l12set='0',l11set='0',l10set='0',l9set='0',l8set='0',l7set='0',l6set='0',l5set='0'";
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
$db->update('nation', [
'capset'=>0,
'l12set'=>0,
'l11set'=>0,
'l10set'=>0,
'l9set'=>0,
'l8set'=>0,
'l7set'=>0,
'l6set'=>0,
'l5set'=>0,
], true);
//관직 변경 제한 해제
$query = "update city set officer4set='0',officer3set='0',officer2set='0'";
MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),"");
$db->update('city', [
'officer4set'=>0,
'officer3set'=>0,
'officer2set'=>0,
], true);
}
// 벌점 감소와 건국제한-1 전턴제한-1 외교제한-1, 1달마다 실행, 병사 있는 장수의 군량 감소, 수입비율 조정
+11
View File
@@ -218,6 +218,17 @@ abstract class BaseCommand{
return $this->arg;
}
public function getGeneral():General{
return $this->generalObj;
}
public function getNationID(){
return $this->generalObj->getNationID();
}
public function getGeneralLevel(){
return $this->generalObj->getVar('level');
}
public function getBrief():string{
return static::getName();
+10 -3
View File
@@ -9,13 +9,15 @@ use \sammo\{
LastTurn,
GameUnitConst,
CityConst,
Command
Command,
TimeUtil
};
use function \sammo\{
getDomesticExpLevelBonus,
CriticalRatioDomestic,
CriticalScoreEx
CriticalScoreEx,
function cutTurn
};
use \sammo\Constraint\Constraint;
@@ -131,7 +133,12 @@ class che_발령 extends Command\NationCommand{
$josaUl = JosaUtil::pick($destGeneralName, '을');
$josaRo = JosaUtil::pick($destCityName, '로');
$destGeneral->getLogger()->pushGeneralActionLog("<Y>{$generalName}</>에 의해 <G><b>{$destCityName}</b></>{$josaRo} 발령됐습니다. <1>$date</>");
$destGeneral->setAuxVar('last발령', $general->getTurnTime());
$yearMonth = Util::joinYearMonth($this->env['year'], $this->env['month']);
if(cutTurn($general->getTurnTime(), $this->env['turnterm']) != cutTurn($destGeneral->getTurnTime(), $this->env['turnterm'])){
$yearMonth+=1;
}
$destGeneral->setAuxVar('last발령', $yearMonth);
$logger->pushGeneralActionLog("<Y>{$destGeneralName}</>{$josaUl} <G><b>{$destCityName}</b></>{$josaRo} 발령했습니다. <1>$date</>");
$this->setResultTurn(new LastTurn(static::getName(), $this->arg));
+214 -185
View File
@@ -65,6 +65,8 @@ class GeneralAI
protected $userWarGenerals;
/** @var General[] */
protected $userCivilGenerals;
/** @var General[] */
protected $chiefGenerals;
/** @var General[] */
protected $lostGenerals;
@@ -96,7 +98,7 @@ class GeneralAI
}
$this->city = $general->getRawCity();
$this->nation = $db->queryFirstRow(
'SELECT nation,name,color,capital,capset,gennum,gold,rice,bill,rate,rate_tmp,scout,war,strategic_cmd_limit,surlimit,tech,power,level,type,aux FROM nation WHERE nation = %i',
'SELECT nation,name,color,capital,capset,gennum,gold,rice,bill,rate,rate_tmp,scout,war,strategic_cmd_limit,surlimit,tech,power,level,l12set,l11set,l10set,l9set,l8set,l7set,l6set,l5set,type,aux FROM nation WHERE nation = %i',
$general->getNationID()
) ?? [
'nation' => 0,
@@ -243,6 +245,9 @@ class GeneralAI
$this->calcWarRoute();
$troopCandidate = [];
$chiefTurn = cutTurn($this->general->getTurnTime(), $this->env['turnterm']);
$yearMonth = Util::joinYearMonth($this->env['year'], $this->env['month']);
foreach($this->troopLeaders as $troopLeader){
$leaderID = $troopLeader->getID();
if(!key_exists($leaderID, $this->nationPolicy->CombatForce)){
@@ -255,6 +260,19 @@ class GeneralAI
continue;
}
$last발령 = $troopLeader->getAuxVar('last발령');
if($last발령){
$leaderTurn = cutTurn($troopLeader->getTurnTime(), $this->env['turnterm']);
$compYearMonth = $yearMonth;
if($chiefTurn < $leaderTurn){
$compYearMonth += 1;
}
if($compYearMonth === $yearMonth){
//한턴마다 한번씩만 발령하자.
continue;
}
}
[$fromCityID, $toCityID] = $this->nationPolicy->CombatForce[$leaderID];
if(!key_exists($fromCityID, $this->warRoute) && !key_exists($toCityID, $this->warRoute)){
@@ -326,6 +344,9 @@ class GeneralAI
if(!$this->frontCities){
return null;
}
$chiefTurn = cutTurn($this->general->getTurnTime(), $this->env['turnterm']);
$yearMonth = Util::joinYearMonth($this->env['year'], $this->env['month']);
$troopCandidate = [];
foreach($this->troopLeaders as $troopLeader){
@@ -344,6 +365,20 @@ class GeneralAI
continue;
}
$last발령 = $troopLeader->getAuxVar('last발령');
if($last발령){
$leaderTurn = cutTurn($troopLeader->getTurnTime(), $this->env['turnterm']);
$compYearMonth = $yearMonth;
if($chiefTurn < $leaderTurn){
$compYearMonth += 1;
}
if($compYearMonth === $yearMonth){
//한턴마다 한번씩만 발령하자.
continue;
}
}
$troopCandidate[$leaderID] = $troopLeader;
}
@@ -708,8 +743,9 @@ class GeneralAI
$cityCandidiates = [];
foreach($this->supplyCities as $city){
$dev = min($city['dev'], 0.999);
$score = (1 - $dev)**2;
$score /= \count($city['generals'])+1;
$score = 1 - $dev;
$score **= 2;
$score /= sqrt(count($city['generals']) + 1);
$cityCandidiates[$city['city']] = $score;
}
@@ -924,8 +960,9 @@ class GeneralAI
$cityCandidiates = [];
foreach($this->supplyCities as $city){
$dev = min($city['dev'], 0.999);
$score = (1 - $dev)**2;
$score /= \count($city['generals'])+1;
$score = 1 - $dev;
$score **= 2;
$score /= sqrt(count($city['generals']) + 1);
$cityCandidiates[$city['city']] = $score;
}
@@ -1667,6 +1704,8 @@ class GeneralAI
return $cmd;
}
}
return null;
}
protected function do전쟁내정(GeneralCommand $reservedCommand): ?GeneralCommand
@@ -1776,6 +1815,10 @@ class GeneralAI
}
}
if(!$cmdList){
return null;
}
return Util::choiceRandomUsingWeightPair($cmdList);
}
@@ -1943,6 +1986,13 @@ class GeneralAI
$type = GameUnitConst::DEFAULT_CREWTYPE;
}
if($this->generalPolicy->can고급병종){
$currType = $general->getCrewTypeObj()->id;
if(key_exists($currType, $typesAll) && $typesAll[$currType][1] >= $typesAll[$type][1]){
$type = $currType;
}
}
//NOTE: 훈련과 사기진작은 '금만 사용한다'는 가정을 하고 있음
$obj훈련 = buildGeneralCommandClass('che_훈련', $general, $env);
$obj사기진작 = buildGeneralCommandClass('che_사기진작', $general, $env);
@@ -2125,10 +2175,9 @@ class GeneralAI
{
$cmd = $reservedCommand;
$general = $this->general;
$generalID = $general->getID();
$cmd = buildGeneralCommandClass('che_집합', $general, $this->env);
_setGeneralCommand($generalID, iterator_to_array(Util::range(GameConst::$maxTurn)), $cmd->getRawClassName(), $cmd->getArg(), $cmd->getBrief());
_setGeneralCommand($cmd, iterator_to_array(Util::range(GameConst::$maxTurn)));
return $cmd;
}
@@ -2140,12 +2189,56 @@ class GeneralAI
protected function do거병(GeneralCommand $reservedCommand): ?GeneralCommand
{
return null;
$general = $this->general;
// 초반이면서 능력이 좋은놈 위주로 1.4%확률로 거병
if($general->getVar('makelimit')){
return null;
}
if(!$this->generalPolicy->can건국){
return null;
}
$prop = Util::randF() * (GameConst::$defaultStatNPCMax + GameConst::$chiefStatMin) / 2;
$ratio = ($this->fullLeadership + $this->fullStrength + $this->fullIntel) / 3;
if($prop >= $ratio){
return null;
}
if(Util::randBool(1 - 0.014)){
return null;
}
$cmd = buildGeneralCommandClass('che_거병', $general, $this->env, null);
if(!$cmd->isRunnable()){
return null;
}
return $cmd;
}
protected function do해산(GeneralCommand $reservedCommand): ?GeneralCommand
{
$cmd = buildGeneralCommandClass('che_해산', $this->general, $this->env, null);
if(!$cmd->isRunnable()){
return null;
}
return $cmd;
}
protected function do건국(GeneralCommand $reservedCommand): ?GeneralCommand
{
return null;
$nationType = Util::choiceRandom(GameConst::$availableNationType);
$nationColor = Util::choiceRandom(array_keys(GetNationColors()));
$cmd = buildGeneralCommandClass('che_건국', $this->general, $this->env, [
'nationName' => "" . mb_substr($this->general->getName(), 1),
'nationType' => $nationType,
'colorType' => $nationColor
]);
if(!$cmd->isRunnable()){
return null;
}
return $cmd;
}
protected function do선양(GeneralCommand $reservedCommand): ?GeneralCommand
@@ -2161,8 +2254,6 @@ class GeneralAI
$db = DB::db();
$arg = null;
// 오랑캐는 바로 임관
if ($general->getVar('npc') == 9) {
$rulerNation = $db->queryFirstField(
@@ -2179,80 +2270,42 @@ class GeneralAI
}
}
switch (Util::choiceRandomUsingWeight([
'임관' => 11.4,
'거병_견문' => 40,
'이동' => 20,
'기타' => 28.6
])) {
//임관
case '임관':
$available = true;
if ($env['startyear'] + 3 > $env['year']) {
//초기 임관 기간에서는 국가가 적을수록 임관 시도가 적음
$nationCnt = $db->queryFirstField('SELECT count(nation) FROM nation');
$notFullNationCnt = $db->queryFirstField('SELECT count(nation) FROM nation WHERE gennum < %i', GameConst::$initialNationGenLimit);
if ($nationCnt == 0 || $notFullNationCnt == 0) {
$available = false;
} else if (Util::randBool(pow(1 / $nationCnt / pow($notFullNationCnt, 3), 1 / 4))) {
//국가가 1개일 경우에는 '임관하지 않음'
$available = false;
}
}
if ($general->getVar('affinity') == 999 || !$available) {
if(Util::randBool(0.3)){
if ($env['startyear'] + 3 > $env['year']) {
//초기 임관 기간에서는 국가가 적을수록 임관 시도가 적음
$nationCnt = $db->queryFirstField('SELECT count(nation) FROM nation');
$notFullNationCnt = $db->queryFirstField('SELECT count(nation) FROM nation WHERE gennum < %i', GameConst::$initialNationGenLimit);
if ($nationCnt == 0 || $notFullNationCnt == 0) {
return null;
}
//랜임 커맨드 입력.
$cmd = buildGeneralCommandClass('che_랜덤임관', $general, $env, ['destNationID' => $rulerNation]);
if(!$cmd->isRunnable()){
return null;
}
return $cmd;
break;
case '거병_견문': //거병이나 견문
// 초반이면서 능력이 좋은놈 위주로 1.4%확률로 거병
if($general->getVar('makelimit')){
return null;
}
if(!$this->generalPolicy->can건국){
return null;
}
$prop = Util::randF() * (GameConst::$defaultStatNPCMax + GameConst::$chiefStatMin) / 2;
$ratio = ($general->getVar('leadership') + $general->getVar('strength') + $general->getVar('intel')) / 3;
if($prop >= $ratio){
if (Util::randBool(pow(1 / $nationCnt / pow($notFullNationCnt, 3), 1 / 4))) {
//국가가 1개일 경우에는 '임관하지 않음'
return null;
}
}
if(Util::randBool(1 - 0.014)){
return null;
}
if ($general->getVar('affinity') == 999) {
return null;
}
$cmd = buildGeneralCommandClass('che_거병', $general, $env, null);
if(!$cmd->isRunnable()){
return null;
}
return $cmd;
break;
case '이동': //이동
//랜임 커맨드 입력.
$cmd = buildGeneralCommandClass('che_랜덤임관', $general, $env, ['destNationID' => $rulerNation]);
if(!$cmd->isRunnable()){
return null;
}
return $cmd;
}
$paths = array_keys(CityConst::byID($city['city'])->path);
if(Util::randBool(0.2)){
$paths = array_keys(CityConst::byID($city['city'])->path);
$cmd = buildGeneralCommandClass('che_이동', $general, $env, ['destCityID' => Util::choiceRandom($paths)]);
if(!$cmd->isRunnable()){
return null;
}
return $cmd;
break;
$cmd = buildGeneralCommandClass('che_이동', $general, $env, ['destCityID' => Util::choiceRandom($paths)]);
if(!$cmd->isRunnable()){
return null;
}
return $cmd;
}
return null;
}
@@ -2382,6 +2435,7 @@ class GeneralAI
$npcCivilGenerals = [];
$npcWarGenerals = [];
$troopLeaders = [];
$chiefGenerals = [];
$nationID = $this->nation['nation'];
@@ -2408,6 +2462,11 @@ class GeneralAI
$generalID = $nationGeneral->getID();
$cityID = $nationGeneral->getCityID();
$npcType = $nationGeneral->getVar('npc');
$generalLevel = $nationGeneral->getVar('level');
if($generalLevel > 4){
$chiefGenerals[$generalLevel] = $nationGeneral;
}
if (key_exists($cityID, $nationCities)) {
$nationCities[$cityID]['generals'][$generalID] = $nationGeneral;
@@ -2461,6 +2520,7 @@ class GeneralAI
$this->npcCivilGenerals = $npcCivilGenerals;
$this->npcWarGenerals = $npcWarGenerals;
$this->troopLeaders = $troopLeaders;
$this->chiefGenerals = $chiefGenerals;
}
@@ -2469,6 +2529,9 @@ class GeneralAI
$lastTurn = $reservedCommand->getLastTurn();
$general = $this->general;
$this->categorizeNationGeneral();
$this->categorizeNationCities();
if($general->getVar('level') == 12){
$month = $this->env['month'];
if (in_array($month, [1, 4, 7, 10])) {
@@ -2481,12 +2544,15 @@ class GeneralAI
$this->calcRiceBillRate();
}
}
else if(in_array($month, [3, 6, 9, 12])){
$this->calcNonLordPromotion();
}
if($reservedCommand->isRunnable()){
return $reservedCommand;
}
$this->categorizeNationGeneral();
foreach($this->nationPolicy->priority as $actionName){
if(!$this->nationPolicy->{'can'.$actionName}){
@@ -2549,29 +2615,34 @@ class GeneralAI
if($result !== null){
return $result;
}
return $this->do중립($reservedCommand);
}
else {
if($npcType >= 2 && $general->getVar('level') == 12 && !$this->nation['capital']){
//방랑군 건국
$result = $this->do건국($reservedCommand);
if($result !== null){
return $result;
}
$result = $this->do방랑군이동($reservedCommand);
if($result !== null){
return $result;
}
if($npcType >= 2 && $general->getVar('level') == 12 && !$this->nation['capital']){
//방랑군 건국
$result = $this->do건국($reservedCommand);
if($result !== null){
return $result;
}
$result = $this->do방랑군이동($reservedCommand);
if($result !== null){
return $result;
}
foreach($this->generalPolicy->priority as $actionName){
if(!$this->generalPolicy->{'can'.$actionName}){
continue;
}
/** @var ?GeneralCommand */
$result = $this->{'do'.$actionName}($reservedCommand);
if($result !== null){
return $result;
}
$result = $this->do해산($reservedCommand);
if($result !== null){
return $result;
}
}
foreach($this->generalPolicy->priority as $actionName){
if(!$this->generalPolicy->{'can'.$actionName}){
continue;
}
/** @var ?GeneralCommand */
$result = $this->{'do'.$actionName}($reservedCommand);
if($result !== null){
return $result;
}
}
@@ -3087,88 +3158,6 @@ class GeneralAI
return Util::choiceRandomUsingWeightPair($commandList);
}
public function chooseNeutralTurn(): array
{
$general = $this->getGeneralObj();
$city = $this->city;
$env = $this->env;
$db = DB::db();
$arg = null;
// 오랑캐는 바로 임관
if ($general->getVar('npc') == 9) {
$rulerNation = $db->queryFirstField(
'SELECT nation FROM general WHERE `level`=12 AND npc=9 and nation not in %li ORDER BY RAND() limit 1',
$general->getAuxVar('joinedNations') ?? [0]
);
if ($rulerNation) {
return ['che_임관', ['destNationID' => $rulerNation]];
}
return ['che_견문', null];
}
switch (Util::choiceRandomUsingWeight([
'임관' => 11.4,
'거병_견문' => 40,
'이동' => 20,
'기타' => 28.6
])) {
//임관
case '임관':
$available = true;
if ($env['startyear'] + 3 > $env['year']) {
//초기 임관 기간에서는 국가가 적을수록 임관 시도가 적음
$nationCnt = $db->queryFirstField('SELECT count(nation) FROM nation');
$notFullNationCnt = $db->queryFirstField('SELECT count(nation) FROM nation WHERE gennum < %i', GameConst::$initialNationGenLimit);
if ($nationCnt == 0 || $notFullNationCnt == 0) {
$available = false;
} else if (Util::randBool(pow(1 / $nationCnt / pow($notFullNationCnt, 3), 1 / 4))) {
//국가가 1개일 경우에는 '임관하지 않음'
$available = false;
}
}
if ($general->getVar('affinity') == 999 || !$available) {
$command = 'che_견문'; //견문
} else {
//랜임 커맨드 입력.
$command = 'che_랜덤임관';
$arg = [
'destNationIDList' => []
];
}
break;
case '거병_견문': //거병이나 견문
// 초반이면서 능력이 좋은놈 위주로 1.4%확률로 거병
$prop = Util::randF() * (GameConst::$defaultStatNPCMax + GameConst::$chiefStatMin) / 2;
$ratio = ($general->getVar('leadership') + $general->getVar('strength') + $general->getVar('intel')) / 3;
if ($env['startyear'] + 2 > $env['year'] && $prop < $ratio && Util::randBool(0.014) && $general->getVar('makelimit') == 0) {
//거병
$command = 'che_거병';
} else {
//견문
$command = 'che_견문';
}
break;
case '이동': //이동
$paths = array_keys(CityConst::byID($city['city'])->path);
$command = 'che_이동';
$arg = ['destCityID' => Util::choiceRandom($paths)];
break;
default:
$command = 'che_견문';
break;
}
return [$command, $arg];
}
public function old_chooseGeneralTurn($command, $arg): array
{
$general = $this->getGeneralObj();
@@ -3205,11 +3194,7 @@ class GeneralAI
$arg = [];
$general->setVar('killturn', rand(70, 75));
//NOTE: 부대 편성에 보여야 하므로 이것만 DB에 직접 접근함.
$db->update('general_turn', [
'action' => 'che_집합',
'arg' => '{}',
'brief' => '집합'
], 'general_ID=%i AND turn_idx < 6', $general->getID());
//_setGeneralCommand($general->getID, [0,1,2,3,4,5], 'che_집합', null, '집합');
}
return [$command, $arg];
@@ -3694,6 +3679,50 @@ class GeneralAI
return Util::choiceRandomUsingWeight($nations);
}
protected function calcNonLordPromotion(){
//빈자리는 아무나 채움
$db = DB::db();
foreach(Util::range(getNationChiefLevel($this->nation['level']), 12) as $chiefLevel){
if($this->nation["l{$chiefLevel}set"]){
continue;
}
if(key_exists($chiefLevel, $this->chiefGenerals)){
continue;
}
$picked = false;
foreach(Util::range(5) as $idx){
/** @var General */
$randGeneral = Util::choiceRandom($this->npcWarGenerals);
if($chiefLevel == 11){
$picked = true;
break;
}
if($chiefLevel % 2 == 0){
if($randGeneral->getStrength(false, false, false, false) < GameConst::$chiefStatMin){
continue;
}
}
else{
if($randGeneral->getIntel(false, false, false, false) < GameConst::$chiefStatMin){
continue;
}
}
$picked = true;
break;
}
if(!$picked){
continue;
}
$randGeneral->setVar('level', $chiefLevel);
$randGeneral->applyDB($db);
}
}
protected function calcPromotion()
{
$db = DB::db();
@@ -3712,7 +3741,7 @@ class GeneralAI
'permission' => 'ambassador',
], 'nation=%i AND npc < 2 AND level > 4', $nationID);
foreach ($db->query(
foreach($db->query(
'SELECT no, npc, level, killturn FROM general WHERE nation = %i AND 12 > level AND level > 4',
$nationID
) as $chief) {
+4
View File
@@ -655,6 +655,10 @@ class Util extends \utilphp\util
return $year * 12 + $month - 1;
}
public static function parseYearMonth(int $yearMonth):array{
return [intdiv($yearMonth, 12), $yearMonth%12 + 1];
}
/**
* 변수의 값을 첫번째 값부터 비교해서 대소를 반환
* 길이가 다른 경우, 앞의 결과를 먼저 비교한 뒤, 짧은쪽의 값을 null으로 가정하여 길이 처리