-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Searcher #3
base: master
Are you sure you want to change the base?
Searcher #3
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
package ru.spbstu.terrai.core | ||
|
||
data class Condition(val items: List<Item>, val exitReached: Boolean) { | ||
constructor(): this(false) | ||
data class Condition(val items: List<Item>, val exitReached: Boolean, val exitFind: Boolean) { | ||
constructor(): this(false, false) | ||
|
||
constructor(exitReached: Boolean): this(emptyList(), exitReached) | ||
constructor(exitReached: Boolean, exitFind: Boolean): this(emptyList(), exitReached, exitFind) | ||
|
||
val hasTreasure get() = Treasure in items | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
package ru.spbstu.terrai.players.samples | ||
|
||
/**попытка модернизации класса DummySeracher'a | ||
* Выход - единственное, в существовании чего можно не сомневаться. | ||
* Created by Numay on 15.04.2017. | ||
*/ | ||
|
||
import ru.spbstu.terrai.core.* | ||
import java.util.* | ||
|
||
class Searcher : AbstractPlayer(){//класс-реализация абстрактного игрока-ИИ | ||
|
||
private lateinit var currentLocation: Location //хранит положение на карте | ||
|
||
private val roomMap = mutableMapOf<Location, Room>()//карта хранящая исследованные ботом клетки | ||
|
||
override fun setStartLocationAndSize(location: Location, width: Int, height: Int) { | ||
super.setStartLocationAndSize(location, width, height) | ||
currentLocation = location | ||
roomMap[currentLocation] = Entrance//стартовая точка - вход | ||
} | ||
|
||
private var lastMove: Move = WaitMove | ||
|
||
private val decisions = mutableListOf<Direction>()//список, хранящий сделанные ходы | ||
|
||
private val pathToExit = mutableListOf<Direction>()//список, хранящий путь от ямы до выхода | ||
|
||
private var wormholes = 0 //считает количество уже пройденных червоточин | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Далее по коду получается, что это очень хакерское поле, и название его не совсем точно |
||
|
||
private var maxholes = 0 //хранит количество червоточин на карте | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. По Code Style -- |
||
|
||
private var pathWasFinded = false //найден ли путь от вормхолла до выхода | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тоже There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В общем и целом, флагов у вас накопилось уже столько, что стоило их все убрать и завести единую переменную-перечисление "Состояние". Его и использовать |
||
|
||
private var treasureFinded = false | ||
//Алгоритм, возвращающий бота к вормхоллу выхода | ||
private var backInTheFirstWormholeFlag = false | ||
//Режим связывания всех существующих вормхоллов с их комнатами | ||
private var bindingFlag = false | ||
private var pathTemp: MutableList<Direction>? = mutableListOf<Direction>() | ||
//хранит последний результат движения | ||
private var lastResult: MoveResult?=null //копия последней полученной информации о состоянии | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Здесь в Котлине лучше так:
Дело в том, что по факту оно всё равно используется как not-null, а null бывает только в самом начале работы |
||
private var loopwWh = false //завис ли бот в цикле | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Это название в принципе неудачное |
||
|
||
//функция управления движением//работает нормально//пока | ||
override fun getNextMove(): Move {//если есть любая клетка, в которой бот еще не был, то он идет туда | ||
|
||
var rng = Random();//для выбора направления | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Генератор случайных чисел лучше определять на верхнем уровне, не создавая его каждый раз заново There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Точки с запятой в Котлине почти никогда не нужны. Также важно употреблять |
||
var toUnknownDirection: Direction? | ||
|
||
//Особые ситуации | ||
|
||
if(wormholes>11){//попытка прервать ушедшего в цикл бота | ||
//не очень | ||
if(loopwWh){//для совсем печальных случаев | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Такие вот ветки программистами называются "подхак". Лучше их избегать. |
||
bindingFlag = false | ||
pathWasFinded = false | ||
} | ||
|
||
|
||
var step: Direction | ||
if(!lastResult!!.successful || lastResult?.room is Wormhole){ | ||
step=Direction.values().first{it + currentLocation !in roomMap} | ||
decisions+=step | ||
lastMove=WalkMove(step) | ||
return lastMove | ||
} | ||
step=decisions.last() | ||
wormholes=0 | ||
roomMap.clear() | ||
lastMove=WalkMove(step.turnBack()) | ||
loopwWh = true | ||
return lastMove | ||
} | ||
|
||
if(backInTheFirstWormholeFlag){//бот нашел клад и должен вернуться ко входу | ||
var step: Direction | ||
if (wormholes!=1){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Почему вдруг именно 1? В чём специальность этого значения? |
||
if(lastResult?.room !is Wormhole && lastResult!!.successful){ | ||
step=decisions.last() | ||
decisions.removeAt(decisions.size - 1) | ||
return WalkMove(step.turnBack()) | ||
} | ||
if(!lastResult!!.successful){ | ||
step=Direction.values().first{it + currentLocation in roomMap} | ||
return WalkMove(step) | ||
} | ||
} | ||
val tempmove = followThePath() | ||
if(tempmove == null){ | ||
return WaitMove | ||
} | ||
return followThePath()!! | ||
} | ||
if(bindingFlag){//бот считает дырки | ||
var tempMove=searching() | ||
if(tempMove!=null) { | ||
lastMove = tempMove | ||
return lastMove | ||
} | ||
bindingFlag=false | ||
} | ||
|
||
//стандартный случай | ||
if(Direction.values().firstOrNull{it + currentLocation !in roomMap} != null) { | ||
do {//направление выбирается случайно, а не по списку | ||
toUnknownDirection = Direction.values().firstOrNull { (it.ordinal == rng.nextInt(4) + 0) && (it + currentLocation !in roomMap)} | ||
} while (toUnknownDirection == null)//это позволит решить лабиринты типа 5го, при условно бесконечном количестве ходов, конечно | ||
} | ||
else toUnknownDirection=null | ||
lastMove = if (toUnknownDirection != null) { | ||
decisions += toUnknownDirection | ||
WalkMove(toUnknownDirection) | ||
} | ||
else {//если нет, то бот идет назад | ||
val lastDecision = decisions.lastOrNull() | ||
if (lastDecision != null) { | ||
decisions.removeAt(decisions.size - 1) | ||
val backDirection = lastDecision.turnBack() | ||
WalkMove(backDirection) | ||
} | ||
else { | ||
WaitMove | ||
} | ||
} | ||
|
||
return lastMove | ||
} | ||
|
||
//создает копию пути от вормхолла до выхода | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Скорее, видимо, не до выхода, а до входа в текущую комнату |
||
private fun getPathToExit (): Move?{ //ну и возвращает бота к вормхоллу откуда он шел | ||
var lastStep: Direction | ||
lastStep = decisions.lastOrNull() ?: return null | ||
decisions.removeAt(decisions.size - 1) | ||
pathToExit+=lastStep | ||
return WalkMove(lastStep.turnBack()) | ||
} | ||
//повторяет путь от вормхолла до выхода, если не находит выход в конце - возвращается к ближайшему вормхоллу | ||
//возвращает null, если выход найден | ||
private fun followThePath (): Move?{ | ||
var step : Direction | ||
//бот следует по запомненному пути к выходу | ||
if(lastResult?.room is Wormhole){//свалился в новый вормхолл -> скопировал маршрут заново | ||
pathTemp = mutableListOf<Direction>() | ||
pathTemp?.addAll(pathToExit) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Не очень понял смысл этой ветки. Можно обсудить при встрече |
||
} | ||
if(!lastResult!!.successful && decisions.size>0)//уперся в стену -> возвратился к вормхоллу | ||
pathTemp = null | ||
step = pathTemp?.firstOrNull() ?: | ||
if (lastResult?.room is Exit) //прошел путь | ||
return null //нашел выход | ||
else {//не нашел выход -> возвращается к точке входа в комнату | ||
step=decisions.last() | ||
decisions.removeAt(decisions.size - 1) | ||
return WalkMove(step.turnBack()) | ||
} | ||
pathTemp?.removeAt(0) | ||
decisions += step | ||
return WalkMove(step) | ||
} | ||
//процесс поиска количества вормхоллов для оптимизации поиска | ||
//возвращает null, когда количество найдено | ||
private fun searching(): Move?{ | ||
//здесь ищется PathToExit | ||
if(!pathWasFinded) { | ||
if (wormholes > 0) { | ||
var newmove = getPathToExit() | ||
if (newmove != null) | ||
return newmove | ||
} | ||
pathWasFinded=true | ||
roomMap.clear()//считаем с самого начала, смысла в старой карте больше нет | ||
wormholes=2;//с этого момента начинается анализ карты, имеет смысл также сбросить счетчик | ||
//но т.к. мы рассматриваем миры в привязке к вормхоллам, то минимальное количество дырок = 2 | ||
} | ||
//пропускаем одну комнату, так как, очевидно, что оттуда вход не найти | ||
if(wormholes==2 || wormholes>11){ | ||
|
||
var step: Direction | ||
if(!lastResult!!.successful || lastResult?.room is Wormhole){ | ||
step=Direction.values().first{it + currentLocation !in roomMap} | ||
decisions+=step | ||
return WalkMove(step) | ||
} | ||
step=decisions.last() | ||
if(wormholes>11){ | ||
wormholes=2 | ||
roomMap.clear() | ||
} | ||
return WalkMove(step.turnBack()) | ||
} | ||
|
||
var lastmove = followThePath() | ||
if (lastmove == null){ | ||
maxholes = wormholes-1//число дырок | ||
return null | ||
} | ||
return lastmove | ||
} | ||
//функция, определяющая алгоритм поведения бота | ||
override fun setMoveResult(result: MoveResult) { | ||
val newLocation = (lastMove as? WalkMove)?.let { it.direction + currentLocation } ?: currentLocation//вычисляет местоположение после хода | ||
val room = result.room//объект, с которым столкнулся игрок | ||
lastResult=result | ||
roomMap[newLocation] = room//запись объекта на карту | ||
if (result.successful) {//если двигался | ||
when(room) { | ||
is Wormhole -> {//если червоточина | ||
if(result.condition.exitFind && !pathWasFinded){//случай, когда бот сначала нашел выход, а потом дырку | ||
pathToExit += decisions | ||
bindingFlag=true//флаг, запускающий алгоритм связывания вормхоллов | ||
} | ||
decisions.clear()//ходы можно выбрасывать | ||
wormholes++ | ||
if(maxholes!=0){//если мы уже знаем сколько всего у нас вормхолов | ||
if(wormholes>maxholes) | ||
wormholes=1;//номер комнаты, по сути | ||
} | ||
currentLocation = Location(wormholes * 100, wormholes * 100) | ||
roomMap[currentLocation] = room | ||
} | ||
is Exit -> {//выход, очевидно | ||
if (!pathWasFinded && wormholes > 0)//когда из червоточины нашел выход | ||
bindingFlag=true | ||
} | ||
else -> {//если клетка пустая | ||
currentLocation = newLocation | ||
if(!treasureFinded && result.condition.hasTreasure){//...после того, как бот в ней пошарил | ||
treasureFinded=true | ||
if(result.condition.exitFind){ | ||
if(maxholes>0) {//если найден и выход, и дырки | ||
backInTheFirstWormholeFlag =true | ||
} | ||
if(wormholes==0) {//есть веростность, что бот уже прошел мимо выхода, нужно сжигать карту | ||
roomMap.clear() | ||
decisions.clear() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
decisions.removeAt(decisions.size - 1)//если стоит на месте | ||
} | ||
} | ||
|
||
} | ||
|
||
|
||
fun main(args: Array<String>) { | ||
|
||
var test = SearcherTest() | ||
test.testLab7() | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package ru.spbstu.terrai.players.samples | ||
|
||
import org.junit.Test | ||
import ru.spbstu.terrai.lab.Controller | ||
|
||
class SearcherTest : AbstractPlayerTest() { | ||
|
||
override fun createPlayer() = Searcher() | ||
|
||
@Test | ||
fun testLab1() { | ||
doTestLab("labyrinths/lab1.txt", Controller.GameResult(15, exitReached = true)) | ||
} | ||
|
||
@Test | ||
fun testLab2() { | ||
doTestLab("labyrinths/lab2.txt", Controller.GameResult(48, exitReached = true)) | ||
} | ||
|
||
@Test | ||
fun testLab3() { | ||
doTestLab("labyrinths/lab3.txt", Controller.GameResult(15, exitReached = true)) | ||
} | ||
|
||
@Test | ||
fun testLab4() { | ||
doTestLab("labyrinths/lab4.txt", Controller.GameResult(43, exitReached = true)) | ||
} | ||
|
||
@Test | ||
fun testLab5() { | ||
doTestLab("labyrinths/lab5.txt", Controller.GameResult(5, exitReached = true)) | ||
} | ||
|
||
@Test | ||
fun testLab6() { | ||
doTestLab("labyrinths/lab6.txt", Controller.GameResult(100, exitReached = true)) | ||
} | ||
|
||
@Test | ||
fun testLab7() { | ||
doTestLab("labyrinths/lab7.txt", Controller.GameResult(100, exitReached = true)) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не
find
, аfound
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кроме этого, поскольку данная штука нужна конкретному игроку, корректнее запоминать её в нём, не меняя внутренние классы игры