org.refcodes.checkerboard.ConsoleCheckerboardViewer Maven / Gradle / Ivy
Show all versions of refcodes-checkerboard Show documentation
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany and licensed
// under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////
package org.refcodes.checkerboard;
import org.refcodes.component.InitializeException;
import org.refcodes.exception.VetoException;
import org.refcodes.mixin.ColumnWidthAccessor.ColumnWidthBuilder;
import org.refcodes.mixin.ColumnWidthAccessor.ColumnWidthProperty;
import org.refcodes.textual.HorizAlignTextMode;
import org.refcodes.textual.TableBuilder;
import org.refcodes.textual.TableStyle;
/**
*
* Extends the interface {@link CheckerboardViewer} with functionality required
* for console output: Most basic implementation of the
* {@link CheckerboardViewer} interface printing the current checkerboard as
* good as it gets. Call {@link #initialize()} when everything is setup
* correctly. When a redraw time <= 0 is set upon construction, then the
* {@link Checkerboard} is printed out upon any according events from the
* underlying {@link Checkerboard} or, in case the refresh time is > 0 the
* {@link Checkerboard} is redrawn as of the refresh loop time. Attention: The
* {@link Checkerboard} is only redrawn in case the {@link Checkerboard} changed
* compared to the last redraw process.
*
* @param the generic type of the {@link Player}
* @param The type which's instances represent a {@link Player} state.
*/
public class ConsoleCheckerboardViewer
, S> extends AbstractCheckerboardViewer
, ConsoleCheckerboardViewer
> implements CheckerboardViewer
>, ColumnWidthProperty, ColumnWidthBuilder> {
// /////////////////////////////////////////////////////////////////////////
// STATICS:
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// CONSTANTS:
// /////////////////////////////////////////////////////////////////////////
private static final int DEFAULT_REFRESH_LOOP_TIME_MILLIS = 100;
private static final int DEFAULT_COLUMN_WIDTH = 3;
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private ConsoleSpriteFactory _spriteFactory;
private int _redrawLoopTimeMillis;
private int _oldState = -1;
private int _columnWidth;
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
/**
* Constructs the {@link ConsoleCheckerboardViewer} with the provided
* {@link SpriteFactory} creating "Sprites" (in this case {@link String}
* instances) for visualizing the playground's state. The
* {@link ConsoleCheckerboardViewer} is initialized with a redraw loop time
* of 100 ms. Attention: The {@link Checkerboard} is only redrawn in case
* the {@link Checkerboard} changed compared to the last redraw process.
*
* @param aCheckerboard The {@link Checkerboard} for which to construct the
* viewer.
* @param aSpriteFactory The {@link SpriteFactory} to be used.
*/
public ConsoleCheckerboardViewer( Checkerboard aCheckerboard, ConsoleSpriteFactory aSpriteFactory ) {
this( aCheckerboard, aSpriteFactory, DEFAULT_REFRESH_LOOP_TIME_MILLIS );
}
/**
* Constructs the {@link ConsoleCheckerboardViewer} with the provided
* {@link SpriteFactory} creating "Sprites" (in this case {@link String}
* instances) for visualizing the playground's state. The
* {@link ConsoleCheckerboardViewer} is initialized with the according
* redraw loop time. When a redraw time <= 0 is set upon construction,
* then the {@link Checkerboard} is printed out upon any according events
* from the underlying {@link Checkerboard} or, in case the refresh time is
* > 0 the {@link Checkerboard} is redrawn as of the refresh loop time.
* Attention: The {@link Checkerboard} is only redrawn in case the
* {@link Checkerboard} changed compared to the last redraw process. A
* default column width of 3 is configured.
*
* @param aRedrawLoopTimeMillis The redraw loop time to work with.
* @param aCheckerboard The {@link Checkerboard} for which to construct the
* viewer.
* @param aSpriteFactory The {@link SpriteFactory} to be used.
*/
public ConsoleCheckerboardViewer( Checkerboard
aCheckerboard, ConsoleSpriteFactory aSpriteFactory, int aRedrawLoopTimeMillis ) {
this( aCheckerboard, aSpriteFactory, DEFAULT_COLUMN_WIDTH, aRedrawLoopTimeMillis );
}
/**
* Constructs the {@link ConsoleCheckerboardViewer} with the provided
* {@link SpriteFactory} creating "Sprites" (in this case {@link String}
* instances) for visualizing the playground's state. The
* {@link ConsoleCheckerboardViewer} is initialized with the according
* redraw loop time. When a redraw time <= 0 is set upon construction,
* then the {@link Checkerboard} is printed out upon any according events
* from the underlying {@link Checkerboard} or, in case the refresh time is
* > 0 the {@link Checkerboard} is redrawn as of the refresh loop time.
* Attention: The {@link Checkerboard} is only redrawn in case the
* {@link Checkerboard} changed compared to the last redraw process.
*
* @param aRedrawLoopTimeMillis The redraw loop time to work with.
* @param aCheckerboard The {@link Checkerboard} for which to construct the
* viewer.
* @param aColumnWidth The column width to be used when drawing the
* {@link Checkerboard} table.
* @param aSpriteFactory The {@link SpriteFactory} to be used.
*/
public ConsoleCheckerboardViewer( Checkerboard
aCheckerboard, ConsoleSpriteFactory aSpriteFactory, int aColumnWidth, int aRedrawLoopTimeMillis ) {
super( aCheckerboard );
aCheckerboard.subscribeObserver( this );
_spriteFactory = aSpriteFactory;
_redrawLoopTimeMillis = aRedrawLoopTimeMillis;
_columnWidth = aColumnWidth;
}
// /////////////////////////////////////////////////////////////////////////
// INJECTION:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public void initialize() throws InitializeException {
if ( _redrawLoopTimeMillis > 0 ) {
Thread t = new Thread( this::printPlaygroundDaemon );
t.setDaemon( true );
t.start();
}
}
// /////////////////////////////////////////////////////////////////////////
// LIFECYLE:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public synchronized void onCheckerboardEvent( CheckerboardEvent
aCheckerboardEvent ) {
onEventPrintPlayground();
}
/**
* {@inheritDoc}
*/
@Override
public void onPlayerAddedEvent( PlayerAddedEvent
aCheckerboardEvent ) {}
/**
* {@inheritDoc}
*/
@Override
public void onPlayerRemovedEvent( PlayerRemovedEvent
aCheckerboardEvent ) {}
/**
* {@inheritDoc}
*/
@Override
public void onGridModeChangedEvent( GridModeChangedEvent
aCheckerboardEvent ) {}
/**
* {@inheritDoc}
*/
@Override
public void onGridDimensionChangedEvent( GridDimensionChangedEvent
aCheckerboardEvent ) {}
/**
* {@inheritDoc}
*/
@Override
public void onViewportOffsetChangedEvent( ViewportOffsetChangedEvent
aCheckerboardEvent ) {}
/**
* {@inheritDoc}
*/
@Override
public void onViewportDimensionChangedEvent( ViewportDimensionChangedEvent
aCheckerboardEvent ) {}
/**
* {@inheritDoc}
*/
@Override
public synchronized void onPlayerEvent( PlayerEvent
aPlayerEvent, Checkerboard
aSource ) {
onEventPrintPlayground();
}
/**
* {@inheritDoc}
*/
@Override
public void onChangePositionEvent( ChangePositionEvent
aPlayerEvent, Checkerboard
aSource ) throws VetoException {}
/**
* {@inheritDoc}
*/
@Override
public void onPositionChangedEvent( PositionChangedEvent
aPlayerEvent, Checkerboard
aSource ) {}
/**
* {@inheritDoc}
*/
@Override
public synchronized void onStateChangedEvent( StateChangedEvent
aPlayerEvent, Checkerboard
aSource ) {
onEventPrintPlayground();
}
/**
* {@inheritDoc}
*/
@Override
public void onVisibilityChangedEvent( VisibilityChangedEvent
aPlayerEvent, Checkerboard
aSource ) {}
/**
* {@inheritDoc}
*/
@Override
public void onDraggabilityChangedEvent( DraggabilityChangedEvent
aPlayerEvent, Checkerboard
aSource ) {}
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
/**
* Retrieves the refresh loop time between two redraw cycles of the
* {@link Checkerboard}. This is the delay time between succeeding print
* outs of the {@link Checkerboard} states to the console. If the refresh
* loop time is > 0, then the {@link Checkerboard} is redrawn at most
* with intervals of the given refresh loop time. Attention: The
* {@link Checkerboard} is only redrawn in case the {@link Checkerboard}
* changed compared to the last redraw process.
*
* @return The according time in milliseconds.
*/
public int getRedrawLoopTimeMillis() {
return _redrawLoopTimeMillis;
}
/**
* Determines whether the {@link Checkerboard} is printed out upon any
* according events from the underlying {@link Checkerboard} or as of the
* refresh loop time (see {@link #getRedrawLoopTimeMillis()}). Attention:
* The {@link Checkerboard} is only redrawn in case the {@link Checkerboard}
* changed compared to the last redraw process.
*
* @return True in case the {@link Checkerboard} is redrawn upon an event
* from the {@link Checkerboard} or upon the refresh loop time.
*/
public boolean isRedrawOnEvent() {
return _redrawLoopTimeMillis <= 0;
}
// /////////////////////////////////////////////////////////////////////////
// HOOKS:
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// HELPER:
// /////////////////////////////////////////////////////////////////////////
/**
* Loops the playground printing via {@link #printPlayground()}.
*/
protected void printPlaygroundDaemon() {
while ( true ) {
printPlayground();
try {
Thread.sleep( _redrawLoopTimeMillis );
}
catch ( InterruptedException ignore ) {}
}
}
/**
* Prints the playground upon an according {@link Checkerboard} event in
* case the
*/
protected void onEventPrintPlayground() {
if ( _redrawLoopTimeMillis <= 0 ) {
printPlayground();
}
}
/**
* Prints the playground to the console with hop counts.
*/
private synchronized void printPlayground() {
if ( _oldState != toState( _checkerboard ) ) {
int the1stWidth = (_checkerboard.getGridHeight() + "").length() + 2;
TableBuilder theBuilder = new TableBuilder().withTableStyle( TableStyle.SINGLE_HEADER_SINGLE_BODY ).withRowWidth( -1 );
theBuilder.addColumn().withColumnHorizAlignTextMode( HorizAlignTextMode.CENTER ).withColumnWidth( the1stWidth );
for ( int x = 0; x < getGridWidth(); x++ ) {
theBuilder.addColumn().withColumnHorizAlignTextMode( HorizAlignTextMode.CENTER ).withColumnWidth( _columnWidth );
}
String[] theRows = new String[getGridWidth() + 1];
theRows[0] = "";
for ( int x = 0; x < getGridWidth(); x++ ) {
theRows[x + 1] = "" + x;
}
theBuilder.printHeader( theRows );
String[] theColumns = new String[getGridWidth() + 1];
P ePlayer;
for ( int y = 0; y < getGridHeight(); y++ ) {
theColumns[0] = "" + Character.valueOf( (char) ('a' + y) );
for ( int x = 0; x < getGridWidth(); x++ ) {
ePlayer = _checkerboard.atPosition( x, y );
if ( ePlayer != null ) {
theColumns[x + 1] = _spriteFactory.createInstance( ePlayer.getStatus(), this );
}
else {
theColumns[x + 1] = "";
}
}
theBuilder.printRow( theColumns );
}
theBuilder.printTail();
System.out.println();
_oldState = toState( _checkerboard );
}
}
/**
* Creates a pseudo hash code from the current {@link Checkerboard} status.
*
* @param aCheckerboard The {@link Checkerboard} for which to create the
* hash code.
*
* @return The according hash code.
*/
private int toState( Checkerboard
aCheckerboard ) {
StringBuilder theBuilder = new StringBuilder();
for ( P ePlayer : _checkerboard.getPlayers() ) {
theBuilder.append( ePlayer.getPositionX() );
theBuilder.append( ePlayer.getPositionY() );
theBuilder.append( ePlayer.getStatus() );
if ( ePlayer.getStatus() != null ) {
theBuilder.append( ePlayer.getStatus().hashCode() );
}
}
return theBuilder.toString().hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public int getColumnWidth() {
return _columnWidth;
}
/**
* {@inheritDoc}
*/
@Override
public void setColumnWidth( int aColumnWidth ) {
_columnWidth = aColumnWidth;
}
/**
* {@inheritDoc}
*/
@Override
public ConsoleCheckerboardViewer
withColumnWidth( int aColumnWidth ) {
setColumnWidth( aColumnWidth );
return this;
}
// /////////////////////////////////////////////////////////////////////////
// INNER CLASSES:
// /////////////////////////////////////////////////////////////////////////
}