Repositioner.groovy

package net.ebdon.trk21;

import static GameSpace.*;
import static Quadrant.*;
import static CourseOffset.*;
/**
 * @file
 * @author      Terry Ebdon
 * @date        January 2019
 * @copyright
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/// @todo Move log format strings into configuration.
/// @todo Replace sprintf() with Log4j2 formatting.
@groovy.util.logging.Log4j2
final class Repositioner {
  final String msgMovePart          = 'Ship move part %2d -';
  final String msgMoveBlocked       = "$msgMovePart object %s at sector %s";
  final String msgCrossQuadEdge     = "$msgMovePart crossing quadrant edge at %s";
  final String msgInEmptySector     = "$msgMovePart in empty sector %s, NewX/Y: ${CourseOffset.format2}";
  final String msgTryToEnterQuad    = "$msgMovePart try to enter quadrant   %s";
  final String msgTryToEnterSect    = "$msgMovePart try to enter sector     %s";
  final String msgConstrainedToQuad = "$msgMovePart constrained to quadrant %s";
  final String msgConstrainedToSect = "$msgMovePart constrained to sector   %s";
  final String msgArrivedInQuad     = 'Ship arrived in quadrant {}';
  final String msgArrivedInsector   = 'Ship arrived in sector   {}';
  final String msgReposInfo         = "Reposition from sector   {} with energy {} and offset {}";
  final String msgJumpFrom          = "Jumping from: quadrant:  {}";
  final String msgfloatQuadFormat   = "[${CourseOffset.format1}, ${CourseOffset.format1}]";
  final String msgJumpTo            = "jumping to:   quadrant: $msgfloatQuadFormat";
  final String msgJumpOffset        = "Jumping with offset: {}";
  final String msgJumpCoord         = "Jump: quadCoord: %d offset: ${CourseOffset.format1} sectCoord: %d";

  boolean moveAborted   = false;
  def           trek    = null;
  // ShipVector    sv      = null;
  CourseOffset  offset  = null;

  float newX; // Line 1840 Stat.2
  float newY; // Line 1840 Stat.3

  def startSector;

  Repositioner( t ) {
    assert t
    trek = t
  }

  String toString() {
    "moveAborted: $moveAborted, newX: $newX, newY: $newY startSector: $startSector\n" +
    "trek=   $trek\n" +
    // "sv=     $sv\n" +
    "offset= $offset"
  }

  void repositionShip( final ShipVector shipVector ) {
    newX = trek.entSectX // Line 1840 Stat.2
    newY = trek.entSectY // Line 1840 Stat.3
    startSector =[ newX, newY ]

    assert trek && shipVector
    moveAborted = false
    // sv = shipVector
    offset = new CourseOffset( shipVector ) // gosub 2300 @ line 1840
    log.info msgReposInfo,
        startSector.toString(), trek.ship.energyUsedByLastMove, offset
    log.debug "Before move: $this"
    trek.with {
      quadrant[ entSectX, entSectY ] = Thing.emptySpace  // Line 1840 Stat.1
      newX = entSectX // Line 1840 Stat.2
      newY = entSectY // Line 1840 Stat.3
    }

    // 1 unit of energy = 1 warp factor & moves ship 1... sector? or quadrant?
    // 1.upto( ship.energyUsedByLastMove ) { // for each sector traversed. L.1860
    for ( int it = 1; it <= trek.ship.energyUsedByLastMove && !moveAborted; ++it ) {
      moveSector it
    }

    trek.with {
      // (entSectX, entSectY) = [newX + 0.5, newY + 0.5]  // Line 1875
      quadrant[ entSectX, entSectY ] = Thing.ship // 1875
      log.info msgArrivedInQuad,   logFmtCoords( entQuadX, entQuadY )
      log.info msgArrivedInsector, logFmtCoords( entSectX, entSectY )
      // log.info "Ship arrived in quadrant ${logFmtCoords( entQuadX, entQuadY )}"
      // log.info "Ship arrived in sector   ${logFmtCoords( entSectX, entSectY )}"
    }
    log.debug "After move: $this"
  }

  private void objectAtSector( subMoveNo, row, col ) {
    log.debug sprintf(
      msgMoveBlocked, subMoveNo,
      trek.quadrant[row,col], logFmtCoords( row, col ) )
    trek.blockedAtSector row, col
    moveAborted = true
  }

  void moveSector( subMoveNo ) {
    assert offset
    int z1, z2 // sector arrived at?
    (z1,z2) = [(newX += offset.x) + 0.5, (newY += offset.y) + 0.5] // Line 1860

    if ( trek.quadrant.contains( z1, z2 ) ) { // Line 1870
      if ( trek.quadrant.isOccupied( z1, z2 ) ) { // Line 1870.2
        objectAtSector subMoveNo, z1, z2
        newX -= offset.x
        newY -= offset.y
      } else {
        trek.entSectX = z1
        trek.entSectY = z2
        log.debug sprintf(
            msgInEmptySector, subMoveNo,
            logFmtCoords( z1, z2 ), newX, newY )
      }
    } else { // Line 1920 - 1925
      log.info sprintf( msgCrossQuadEdge, subMoveNo, logFmtCoords( z1, z2 ) )
      // log.info "Ship move part $subMoveNo - crossing quadrant edge at ${logFmtCoords( z1, z2 )}"
      moveAborted = true
      trek.with {
        log.info "Resetting sector to $startSector"
        (entSectX,entSectY) = startSector
        // z1 = newX = newCoordIfOutsideQuadrant( sv, entSectX, offset.x ) // 1920, 1925 stat 1
        // z2 = newY = newCoordIfOutsideQuadrant( sv, entSectY, offset.y ) // 1920, 1925 stat 2

        // (z1,z2,newX,newY) = newCoordIfOutsideQuadrant() // 1920, 1925 stat 2
        (entQuadX,entQuadY) = newCoordIfOutsideQuadrantV2() // 1920, 1925 stat 2

        // entSectX  = bounceToSectCoord( newX, z1 ) // Line 1925, stats 3.
        // entSectY  = bounceToSectCoord( newY, z2 ) // Line 1925, stats 4.

        log.info sprintf( msgTryToEnterQuad, subMoveNo, logFmtCoords( entQuadX,entQuadY ) )
        log.info sprintf( msgTryToEnterSect, subMoveNo, logFmtCoords( entSectX,entSectY ) )
        // log.info "Ship move part $subMoveNo - try to enter quadrant ${[z1,z2]} == $z2 - $z1"
        // (entQuadX,entQuadY) = constrainCoords( [z1, z2] ) // 1930 & 1940 -- Can't leave galaxy.
        (entQuadX,entQuadY) = constrainCoords( [entQuadX,entQuadY] ) // 1930 & 1940 -- Can't leave galaxy.
        (entSectX,entSectY) = constrainCoords( [entSectX,entSectY] ) // 1930 & 1940 -- Can't leave galaxy.
        log.info sprintf( msgConstrainedToQuad, subMoveNo, logFmtCoords( entQuadX,entQuadY ) )
        log.info sprintf( msgConstrainedToSect, subMoveNo, logFmtCoords( entSectX,entSectY ) )
        // log.info "Ship move part $subMoveNo - constrained to quadrant ${[z1,z2]} == $z2 - $z1"
      }
    }
  }

  /// @arg compoundCoord - float of form q.sss where q is a quadrantt No.
  ///   and sss is the sector expressed as a fraction of a quadrant.
  ///   e.g. 1.125 is quadrant 1, sector 1
  /// @arg quadrantCoord - The quadrant coord as an integer.
  /// @return A sector coordinate
  /// @bug The `+ 0.5`, to round, is pointles as integer conversion truncates.
  static int bounceToSectCoord( float compoundCoord, int quadrantCoord ) {
    maxCoord * ( compoundCoord - quadrantCoord ) + 0.5  // Line 1925, stats 3 and 4.
  }

  /// Returns the new coordinates as floats of the form:
  /// `q.sss` where `q` is the new quadrant and `sss` is the sector,
  /// expressed as eights of a quadrant. e.g. 2.125 means 2 quadrants and 1
  /// sector.
  // def newCoordIfOutsideQuadrant() {
  //   def rv = []
  //   [ [trek.entQuadX, offset.x, trek.entSectX],
  //     [trek.entQuadY, offset.y, trek.entSectY]
  //   ].each { quadCoord, offset, sectCoord -> // Line 1920
  //     rv << quadCoord + sv.warpFactor * offset + ( sectCoord -0.5 ) / 8
  //   }
  //   rv * 2
  // }

  /// Returns:
  ///  [quadrantRow,quadrantCol.sectorRow,sectorCol]
  def newCoordIfOutsideQuadrantV2() {
    log.info msgJumpFrom, logFmtCoords( trek.entQuadX, trek.entQuadY )
    log.info msgJumpOffset, offset
    def rQuadCoords = []
    final def warpFactor = offset.shipVector.warpFactor
    // def rSectCoords = []
    [ [trek.entQuadX, offset.x, trek.entSectX],
      [trek.entQuadY, offset.y, trek.entSectY]
    ].each { quadCoord, offsetCoord, sectCoord -> // Line 1920
      // log.debug "quadCoord: $quadCoord warp: ${sv.warpFactor} offset: $offset sectCoord: $sectCoord"
      log.debug sprintf( msgJumpCoord, quadCoord, offsetCoord, sectCoord )
      rQuadCoords << quadCoord + warpFactor * offsetCoord + (sectCoord - 0.5) / 8
      // rSectCoords << ( sectCoord -0.5 ) / 8
    }
    log.info sprintf( msgJumpTo, *rQuadCoords )
    rQuadCoords
  }

  // static float newCoordIfOutsideQuadrant( final ShipVector sv, final coord, final offset ) {
  //   coord + sv.warpFactor * offset + ( coord -0.5 ) / 8 // Line 1920
  // }
}