olistic / warriorjs

🏰 An exciting game of programming and Artificial Intelligence
https://warriorjs.com
MIT License
9.41k stars 489 forks source link

Bug with `feel()` at the very first turn #211

Closed lipsumar closed 6 years ago

lipsumar commented 6 years ago

There seems to be bug when using feel at the very first turn. For some reason it thinks there is an enemy where there is none.

Environment

System:

Steps to reproduce

At level 2, use this code:

class Player {
  playTurn(warrior) {
    const space = warrior.feel()
    if (space.isUnit()) {
      const unit = space.getUnit()
      if (unit.isEnemy()) {
        throw new Error('Unexpected enemy')
      }
    }
    warrior.walk()
  }
}

Result:

╔════════╗
║@   s  >║
╚════════╝
Invalid Player code: Unexpected enemy

Throwing an error is the only way i found to get feedback, warrior.think doesn't output anything at that point (very first turn).

Expected Behavior

space.isUnit() should return false

Actual Behavior

space.isUnit() returns true at the very first turn, even if the space in front of the warrior is clearly empty.

olistic commented 6 years ago

Hello @lipsumar. Thanks for opening the issue, but this isn't a bug. Let me explain:

The playTurn method execution and the output you see in the screen are completely disconnected. First, the playTurn method of each unit in the level (including your warrior) is executed (here and here). This sets the action to be performed on the unit's turn. Then, the performTurn method of each unit is executed, which finally performs the action selected for that turn (here and here). This is where play log entries like "Starbolt walks forward" are added, but they are not printed to the screen at this point. At the end of the entire play, the play log is returned and printed to the screen by the CLI. If you manually throw an error during that process, you're basically preventing the play log from being returned and then printed to the screen.

If you change your code to this, you'll see it behaves correctly:

class Player {
  playTurn(warrior) {
    const space = warrior.feel()
    if (space.isUnit()) {
      const unit = space.getUnit()
      if (unit.isEnemy()) {
        warrior.think('there is an enemy in front')
        warrior.attack()
      }
    } else {
      warrior.walk()
    }
  }
}