/**
* Copyright (c) 2005-2017, KoLmafia development team
* http://kolmafia.sourceforge.net/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* [1] Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* [2] Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* [3] Neither the name "KoLmafia" nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package net.sourceforge.kolmafia.session;
import java.util.Arrays;
import net.sourceforge.kolmafia.AdventureResult;
import net.sourceforge.kolmafia.KoLCharacter;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.preferences.Preferences;
public abstract class LouvreManager
{
// Range of choice numbers within the LouvreManager
private static final int FIRST_CHOICE = 904;
private static final int LAST_CHOICE = 913;
// There is a LouvreManager Map at:
//
// http://i224.photobucket.com/albums/dd259/abeaS_oyR/kollouvre.jpg
// The various locations within the LouvreManager
private static final String LouvreLocationNames [] =
{
"Relativity", // 904
"The Persistence of Memory: shoes/Dancin' Fool (buff), Venus, Piet Mondrian", // 905
"Piet Mondrian: Scream, Moxie, Adam", // 906
"The Scream: Relativity, Venus, Manetwich", // 907
"The Birth of Venus: Swordholder (buff), Nighthawks, Adam", // 908
"The Creation of Adam: Socrates, Muscle, Sunday Afternoon", // 909
"The Death of Socrates: Relativity, Nighthawks, Vangoghbitussin", // 910
"Nighthawks: Is This Your Card? (buff), Persistence of Memory, Sunday Afternoon", // 911
"Sunday Afternoon on the Island of La Grande Jatte: The Last Supper, Mysticality, Piet Mondrian", // 912
"The Last Supper: Relativity, Persistence of Memory, Pinot Renoir", // 913
};
private static final String LouvreShortLocationNames [] =
{
"Relativity", // 904
"The Persistence of Memory", // 905
"Piet Mondrian", // 906
"The Scream", // 907
"The Birth of Venus", // 908
"The Creation of Adam", // 909
"The Death of Socrates", // 910
"Nighthawks", // 911
"Sunday Afternoon on the Island of La Grande Jatte", // 912
"The Last Supper", // 913
};
// 0 = 92, 93, 94, or 95
private static final int LouvreLocationExits [][] =
{
{ 0, 0, 0 }, // 904
{ 7, 908, 906 }, // 905
{ 907, 6, 909 }, // 906
{ 904, 908, 1 }, // 907
{ 8, 911, 909 }, // 908
{ 910, 4, 912 }, // 909
{ 904, 911, 2 }, // 910
{ 9, 905, 912 }, // 911
{ 913, 5, 906 }, // 912
{ 904, 905, 3 }, // 913
};
public static final String LouvreGoals [] =
{
"Manetwich",
"bottle of Vangoghbitussin",
"bottle of Pinot Renoir",
"Muscle",
"Mysticality",
"Moxie",
"Lady Spookyraven's shoes/Dancin' Fool (buff)",
"Swordholder (buff)",
"Is This Your Card? (buff)",
};
public static final AdventureResult LouvreGoalItems[] =
{
ItemPool.get( ItemPool.MANETWICH, 1 ),
ItemPool.get( ItemPool.VANGOGHBITUSSIN, 1 ),
ItemPool.get( ItemPool.PINOT_RENOIR, 1 ),
};
// Identifying strings from the response text
public static final String LouvreGoalStrings [] =
{
"Manetwich",
"bottle of Vangoghbitussin",
"bottle of Pinot Renoir",
"a pretty good workout.",
"new insight as to the nature of the universe.",
"Moxious!",
"with a cane",
"Swordholder",
"Is This Your Card?",
};
// The choice table.
//
// One row for each LouvreManager location (92 - 104)
// Each row contains three values, corresponding to choices 1 - 3
//
// 0 Unknown
// 1 - 9 A goal
// X A destination
private static final int[] choiceTuple( final int source )
{
if ( !LouvreManager.louvreChoice( source ) )
{
return null;
}
return LouvreManager.LouvreLocationExits[ source - LouvreManager.FIRST_CHOICE ];
}
public static final boolean louvreChoice( final int choice )
{
return choice >= LouvreManager.FIRST_CHOICE && choice <= LouvreManager.LAST_CHOICE;
}
public static final void resetDecisions()
{
for ( int i = 0; i < LouvreManager.LouvreGoalItems.length; ++i )
{
if ( GoalManager.hasGoal( LouvreManager.LouvreGoalItems[ i ] ) )
{
Preferences.setInteger( "louvreGoal", i + 1 );
return;
}
}
int goal = Preferences.getInteger( "louvreDesiredGoal" );
if ( goal == LouvreManager.LouvreGoals.length + 1 )
{
if ( KoLCharacter.isMuscleClass() )
{
Preferences.setInteger( "louvreGoal", 4 );
}
else if ( KoLCharacter.isMysticalityClass() )
{
Preferences.setInteger( "louvreGoal", 5 );
}
else
{
Preferences.setInteger( "louvreGoal", 6 );
}
}
else if ( goal == LouvreManager.LouvreGoals.length + 2 )
{
// Compare total subpoints acquired, rather than the
// non-raw, calculated value for comparing which stat
// should be chosen next.
long mus = KoLCharacter.getTotalMuscle();
long mys = KoLCharacter.getTotalMysticality();
long mox = KoLCharacter.getTotalMoxie();
if ( mus <= mys && mus <= mox )
{
Preferences.setInteger( "louvreGoal", 4 );
}
else if ( mys <= mus && mys <= mox )
{
Preferences.setInteger( "louvreGoal", 5 );
}
else
{
Preferences.setInteger( "louvreGoal", 6 );
}
}
else
{
Preferences.setInteger( "louvreGoal", goal );
}
}
private static final String currentGoalString()
{
LouvreManager.resetDecisions();
int goal = Preferences.getInteger( "louvreGoal" );
if ( goal <= 0 || goal > LouvreManager.LouvreGoals.length )
{
return "unknown";
}
return LouvreManager.LouvreGoals[ goal - 1 ];
}
public static final String handleChoice( final int source, final int stepCount )
{
// We only handle LouvreManager choices
if ( !LouvreManager.louvreChoice( source ) )
{
return "";
}
String override = Preferences.getString( "louvreOverride" );
if ( override.contains( "," ) )
{
String[] options = override.split( "\\s*,\\s*" );
if ( options.length > stepCount )
{
if ( options[ stepCount ].equalsIgnoreCase( "up" ) )
{
return "1";
}
else if ( options[ stepCount ].equalsIgnoreCase( "down" ) )
{
return "2";
}
else
{
return "3";
}
}
}
// Get the routing tuple for this choice/goal
LouvreManager.resetDecisions();
int goal = Preferences.getInteger( "louvreGoal" );
// Pick the best choice
return LouvreManager.pickNewExit( source, goal );
}
// Node marking to prevent loops
private static final boolean NodeMarks[] = new boolean[ LouvreManager.LAST_CHOICE - LouvreManager.FIRST_CHOICE + 1 ];
private static final String pickNewExit( final int source, final int goal )
{
// Examine destinations and take shortest known path to goal
int[] choices = LouvreManager.choiceTuple( source );
int choice = 0;
int hops = Integer.MAX_VALUE;
for ( int i = 0; i < choices.length; ++i )
{
// Clear marks on nodes
Arrays.fill( LouvreManager.NodeMarks, false );
// Mark this node
LouvreManager.NodeMarks[ source - LouvreManager.FIRST_CHOICE ] = true;
// Determine how far destination is from goal
int destination = choices[ i ];
int dist = LouvreManager.hopsTo( 0, source, destination, goal );
if ( dist < hops )
{
choice = i;
hops = dist;
}
}
return String.valueOf( choice + 1 );
}
private static final int hopsTo( int hops, final int source, int destination, final int goal )
{
if ( destination == 0 )
{
// This only applies to the starting noncombat. Add 20 hops to prefer not using it.
// Set an arbitrary destination for prediction purposes
hops += 20;
destination = LouvreLocationExits[ source - LouvreManager.FIRST_CHOICE ][ 0 ];
}
// If destination is the goal, we're there
if ( destination == goal )
{
return hops;
}
// If destination is another goal, can't get there from here
if ( destination >= 1 && destination <= LouvreManager.LouvreGoals.length )
{
return Integer.MAX_VALUE;
}
// If destination is a predicted but unmapped Escher node (all other
// possibilities should have been eliminated above), we can reach any
// goal, but prefer a more direct route.
if ( !LouvreManager.louvreChoice( destination ) )
{
return hops + 100;
}
// Known destination. If we've been here before, punt
if ( LouvreManager.NodeMarks[ destination - LouvreManager.FIRST_CHOICE ] )
{
return Integer.MAX_VALUE;
}
// Known destination visited for first time
LouvreManager.NodeMarks[ destination - LouvreManager.FIRST_CHOICE ] = true;
// Examine destinations and take shortest known path to goal
int[] choices = LouvreManager.choiceTuple( destination );
int nextHops = Integer.MAX_VALUE;
for ( int i = 0; i < choices.length; ++i )
{
// Determine how far destination is from goal
int dist = LouvreManager.hopsTo( hops + 1, destination, choices[ i ], goal );
if ( dist < nextHops )
{
nextHops = dist;
}
}
return nextHops;
}
public static final String[][] choiceSpoilers( final int choice )
{
// We only handle LouvreManager choices
if ( !LouvreManager.louvreChoice( choice ) )
{
return null;
}
// Return an array with the same structure as used by built-in
// choice adventures.
String[][] result = new String[ 3 ][];
// The choice option is the first element
result[ 0 ] = new String[ 1 ];
result[ 0 ][ 0 ] = "choiceAdventure" + String.valueOf( choice );
// The name of the choice is second element
result[ 1 ] = new String[ 1 ];
result[ 1 ][ 0 ] = LouvreManager.LouvreLocationNames[ choice - LouvreManager.FIRST_CHOICE ];
// An array of choice spoilers is the third element
int choices[] = LouvreManager.choiceTuple( choice );
result[ 2 ] = new String[ 3 ];
result[ 2 ][ 0 ] = LouvreManager.choiceName( choices[ 0 ] );
result[ 2 ][ 1 ] = LouvreManager.choiceName( choices[ 1 ] );
result[ 2 ][ 2 ] = LouvreManager.choiceName( choices[ 2 ] );
return result;
}
public static final String encounterName( final int choice )
{
if ( !louvreChoice( choice ) )
{
return "";
}
String name = LouvreManager.LouvreShortLocationNames[ choice - LouvreManager.FIRST_CHOICE ];
return "Louvre It or Leave It (" + name + ")";
}
private static final String choiceName( final int destination )
{
switch ( destination )
{
case 0:
return "";
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
return LouvreManager.LouvreGoals[ destination - 1 ];
default:
return LouvreManager.LouvreLocationNames[ destination - LouvreManager.FIRST_CHOICE ];
}
}
public static final void addGoalButton( final StringBuffer buffer )
{
String goal = LouvreManager.currentGoalString();
ChoiceManager.addGoalButton( buffer, goal );
}
}