/**
* 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.maximizer;
import java.util.ArrayList;
import java.util.TreeMap;
import net.sourceforge.kolmafia.AdventureResult;
import net.sourceforge.kolmafia.FamiliarData;
import net.sourceforge.kolmafia.KoLCharacter;
import net.sourceforge.kolmafia.KoLConstants;
import net.sourceforge.kolmafia.KoLConstants.WeaponType;
import net.sourceforge.kolmafia.KoLmafia;
import net.sourceforge.kolmafia.Modifiers;
import net.sourceforge.kolmafia.Speculation;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.persistence.EquipmentDatabase;
import net.sourceforge.kolmafia.persistence.ItemDatabase;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.request.EquipmentRequest;
import net.sourceforge.kolmafia.session.EquipmentManager;
import net.sourceforge.kolmafia.utilities.BooleanArray;
public class MaximizerSpeculation
extends Speculation
implements Comparable<MaximizerSpeculation>, Cloneable
{
private boolean scored = false;
private boolean tiebreakered = false;
private boolean exceeded;
private double score, tiebreaker;
private int simplicity;
private int beeosity;
public boolean failed = false;
public CheckedItem attachment;
private boolean foldables = false;
@Override
public Object clone()
{
try
{
MaximizerSpeculation copy = (MaximizerSpeculation) super.clone();
copy.equipment = (AdventureResult[]) this.equipment.clone();
return copy;
}
catch ( CloneNotSupportedException e )
{
return null;
}
}
@Override
public String toString()
{
if ( this.attachment != null )
{
return this.attachment.getInstance( (int) this.getScore() ).toString();
}
return super.toString();
}
public void setUnscored()
{
this.scored = false;
this.calculated = false;
}
public double getScore()
{
if ( this.scored ) return this.score;
if ( !this.calculated ) this.calculate();
this.score = Maximizer.eval.getScore( this.mods );
if ( KoLCharacter.inBeecore() )
{
this.beeosity = KoLCharacter.getBeeosity( this.equipment );
}
Maximizer.eval.checkEquipment( this.mods, this.equipment,
this.beeosity );
this.failed = Maximizer.eval.failed;
if ( (this.mods.getRawBitmap( Modifiers.MUTEX_VIOLATIONS )
& ~KoLCharacter.currentRawBitmapModifier( Modifiers.MUTEX_VIOLATIONS )) != 0 )
{ // We're speculating about something that would create a
// mutex problem that the player didn't already have.
this.failed = true;
}
this.exceeded = Maximizer.eval.exceeded;
this.scored = true;
return this.score;
}
public double getTiebreaker()
{
if ( this.tiebreakered ) return this.tiebreaker;
if ( !this.calculated ) this.calculate();
this.tiebreaker = Maximizer.eval.getTiebreaker( this.mods );
this.tiebreakered = true;
this.simplicity = 0;
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
AdventureResult item = this.equipment[ slot ];
if ( item == null ) item = EquipmentRequest.UNEQUIP;
if ( EquipmentManager.getEquipment( slot ).equals( item ) )
{
this.simplicity += 2;
}
else if ( item.equals( EquipmentRequest.UNEQUIP ) )
{
this.simplicity += slot == EquipmentManager.WEAPON ? -1 : 1;
}
}
return this.tiebreaker;
}
public int compareTo( MaximizerSpeculation o )
{
if ( !(o instanceof MaximizerSpeculation) ) return 1;
MaximizerSpeculation other = (MaximizerSpeculation) o;
int rv = Double.compare( this.getScore(), other.getScore() );
if ( this.failed != other.failed ) return this.failed ? -1 : 1;
if ( rv != 0 ) return rv;
rv = other.beeosity - this.beeosity;
if ( rv != 0 ) return rv;
rv = Double.compare( this.getTiebreaker(), other.getTiebreaker() );
if ( rv != 0 ) return rv;
rv = this.simplicity - other.simplicity;
if ( rv != 0 ) return rv;
// prefer more rollover effects and fewer breakable items
int countThisEffects = 0;
int countOtherEffects = 0;
int countThisBreakables = 0;
int countOtherBreakables = 0;
for ( int i = this.equipment.length - 1; i >= 0; --i )
{
if ( this.equipment[ i ] == null ) continue;
int itemId = this.equipment[ i ].getItemId();
Modifiers mods = Modifiers.getItemModifiers( itemId );
if ( mods == null ) continue;
String name = mods.getString( Modifiers.ROLLOVER_EFFECT );
if ( name.length() > 0 ) countThisEffects++;
name = mods.getString( Modifiers.BREAKABLE );
if ( name.length() > 0 ) countThisBreakables++;
}
for ( int i = other.equipment.length - 1; i >= 0; --i )
{
if ( other.equipment[ i ] == null ) continue;
int itemId = other.equipment[ i ].getItemId();
Modifiers mods = Modifiers.getItemModifiers( itemId );
if ( mods == null ) continue;
String name = mods.getString( Modifiers.ROLLOVER_EFFECT );
if ( name.length() > 0 ) countOtherEffects++;
name = mods.getString( Modifiers.BREAKABLE );
if ( name.length() > 0 ) countOtherBreakables++;
}
if ( countThisEffects != countOtherEffects )
{
return countThisEffects > countOtherEffects ? 1 : -1;
}
if ( countThisBreakables != countOtherBreakables )
{
return countThisBreakables < countOtherBreakables ? 1 : -1;
}
if ( this.attachment != null && other.attachment != null )
{
// prefer items that you don't have to buy
if ( this.attachment.buyableFlag != other.attachment.buyableFlag )
{
return this.attachment.buyableFlag ? -1 : 1;
}
if ( KoLCharacter.inBeecore() )
{ // prefer fewer Bs
rv = KoLCharacter.getBeeosity( other.attachment.getName() ) -
KoLCharacter.getBeeosity( this.attachment.getName() );
}
// prefer items that you have
// doesn't consider wanting multiple of the same item and not having enough
if ( ( this.attachment.inventory > 0 ) != ( other.attachment.inventory > 0 ) )
{
return this.attachment.inventory > 0 ? 1 : -1;
}
if ( ( this.attachment.initial > 0 ) != ( other.attachment.initial > 0 ) )
{
return this.attachment.initial > 0 ? 1 : -1;
}
}
return rv;
}
// Remember which equipment slots were null, so that this
// state can be restored later.
public Object mark()
{
return this.equipment.clone();
}
public void restore( Object mark )
{
System.arraycopy( mark, 0, this.equipment, 0, EquipmentManager.ALL_SLOTS );
}
public void tryAll( ArrayList familiars, ArrayList enthronedFamiliars, BooleanArray usefulOutfits, TreeMap outfitPieces, ArrayList[] possibles,
AdventureResult bestCard, FamiliarData useCrownFamiliar, FamiliarData useBjornFamiliar )
throws MaximizerInterruptedException
{
this.foldables = Preferences.getBoolean( "maximizerFoldables" );
this.tryOutfits( enthronedFamiliars, usefulOutfits, outfitPieces, possibles, bestCard, useCrownFamiliar, useBjornFamiliar );
for ( int i = 0; i < familiars.size(); ++i )
{
this.setFamiliar( (FamiliarData) familiars.get( i ) );
possibles[ EquipmentManager.FAMILIAR ] =
possibles[ EquipmentManager.ALL_SLOTS + i ];
this.tryOutfits( enthronedFamiliars, usefulOutfits, outfitPieces, possibles, bestCard, useCrownFamiliar, useBjornFamiliar );
}
}
public void tryOutfits( ArrayList<FamiliarData> enthronedFamiliars, BooleanArray usefulOutfits, TreeMap outfitPieces, ArrayList[] possibles,
AdventureResult bestCard, FamiliarData useCrownFamiliar, FamiliarData useBjornFamiliar )
throws MaximizerInterruptedException
{
Object mark = this.mark();
for ( int outfit = usefulOutfits.size() - 1; outfit >= 0; --outfit )
{
if ( !usefulOutfits.get( outfit ) ) continue;
AdventureResult[] pieces = EquipmentDatabase.getOutfit( outfit ).getPieces();
pieceloop:
for ( int idx = pieces.length - 1; ; --idx )
{
if ( idx == -1 )
{ // all pieces successfully put on
this.tryFamiliarItems( enthronedFamiliars, possibles, bestCard, useCrownFamiliar, useBjornFamiliar );
break;
}
AdventureResult item = (AdventureResult) outfitPieces.get( pieces[ idx ] );
if ( item == null ) break; // not available
int count = item.getCount();
int slot = EquipmentManager.itemIdToEquipmentType( item.getItemId() );
switch ( slot )
{
case EquipmentManager.HAT:
case EquipmentManager.PANTS:
case EquipmentManager.SHIRT:
if ( item.equals( this.equipment[ slot ] ) )
{ // already worn
continue pieceloop;
}
if ( item.equals( this.equipment[ EquipmentManager.FAMILIAR ] ) )
{
--count;
}
break;
case EquipmentManager.WEAPON:
case EquipmentManager.OFFHAND:
if ( item.equals( this.equipment[ EquipmentManager.WEAPON ] ) ||
item.equals( this.equipment[ EquipmentManager.OFFHAND ] ) )
{ // already worn
continue pieceloop;
}
if ( item.equals( this.equipment[ EquipmentManager.FAMILIAR ] ) )
{
--count;
}
break;
case EquipmentManager.ACCESSORY1:
if ( item.equals( this.equipment[ EquipmentManager.ACCESSORY1 ] ) ||
item.equals( this.equipment[ EquipmentManager.ACCESSORY2 ] ) ||
item.equals( this.equipment[ EquipmentManager.ACCESSORY3 ] ) )
{ // already worn
continue pieceloop;
}
if ( item.equals( this.equipment[ EquipmentManager.FAMILIAR ] ) )
{
--count;
}
if ( this.equipment[ EquipmentManager.ACCESSORY3 ] == null )
{
slot = EquipmentManager.ACCESSORY3;
}
else if ( this.equipment[ EquipmentManager.ACCESSORY2 ] == null )
{
slot = EquipmentManager.ACCESSORY2;
}
break;
default:
break pieceloop; // don't know how to wear that
}
if ( count <= 0 ) break; // none available
if ( this.equipment[ slot ] != null ) break; // slot taken
this.equipment[ slot ] = item;
}
this.restore( mark );
}
this.tryFamiliarItems( enthronedFamiliars, possibles, bestCard, useCrownFamiliar, useBjornFamiliar );
}
public void tryFamiliarItems( ArrayList<FamiliarData> enthronedFamiliars, ArrayList[] possibles, AdventureResult bestCard,
FamiliarData useCrownFamiliar, FamiliarData useBjornFamiliar )
throws MaximizerInterruptedException
{
Object mark = this.mark();
if ( this.equipment[ EquipmentManager.FAMILIAR ] == null )
{
ArrayList possible = possibles[ EquipmentManager.FAMILIAR ];
boolean any = false;
for ( int pos = 0; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
int count = item.getCount();
if ( item.equals( this.equipment[ EquipmentManager.OFFHAND ] ) )
{
--count;
}
if ( item.equals( this.equipment[ EquipmentManager.WEAPON ] ) )
{
--count;
}
if ( item.equals( this.equipment[ EquipmentManager.HAT ] ) )
{
--count;
}
if ( item.equals( this.equipment[ EquipmentManager.PANTS ] ) )
{
--count;
}
ArrayList group = ItemDatabase.getFoldGroup( item.getName() );
if ( group != null && this.foldables )
{
String groupName = (String) group.get( 1 );
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
if ( slot != EquipmentManager.FAMILIAR && this.equipment[ slot ] != null )
{
ArrayList groupEquipped = ItemDatabase.getFoldGroup( this.equipment[ slot ].getName() );
if ( groupEquipped != null && groupName.equals( (String) groupEquipped.get( 1 ) ) )
{
--count;
}
}
}
}
if ( count <= 0 ) continue;
this.equipment[ EquipmentManager.FAMILIAR ] = item;
this.tryContainers( enthronedFamiliars, possibles, bestCard, useCrownFamiliar, useBjornFamiliar );
any = true;
this.restore( mark );
}
if ( any ) return;
this.equipment[ EquipmentManager.FAMILIAR ] = EquipmentRequest.UNEQUIP;
}
this.tryContainers( enthronedFamiliars, possibles, bestCard, useCrownFamiliar, useBjornFamiliar );
this.restore( mark );
}
public void tryContainers( ArrayList<FamiliarData> enthronedFamiliars, ArrayList[] possibles, AdventureResult bestCard,
FamiliarData useCrownFamiliar, FamiliarData useBjornFamiliar )
throws MaximizerInterruptedException
{
Object mark = this.mark();
if ( this.equipment[ EquipmentManager.CONTAINER ] == null )
{
ArrayList possible = possibles[ EquipmentManager.CONTAINER ];
boolean any = false;
for ( int pos = 0; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
int count = item.getCount();
ArrayList group = ItemDatabase.getFoldGroup( item.getName() );
if ( group != null && this.foldables )
{
String groupName = (String) group.get( 1 );
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
if ( slot != EquipmentManager.CONTAINER && this.equipment[ slot ] != null )
{
ArrayList groupEquipped = ItemDatabase.getFoldGroup( this.equipment[ slot ].getName() );
if ( groupEquipped != null && groupName.equals( (String) groupEquipped.get( 1 ) ) )
{
--count;
}
}
}
}
if ( count <= 0 ) continue;
this.equipment[ EquipmentManager.CONTAINER ] = item;
if ( item.getItemId() == ItemPool.BUDDY_BJORN )
{
if ( useBjornFamiliar != FamiliarData.NO_FAMILIAR )
{
this.setBjorned( useBjornFamiliar );
this.tryAccessories( enthronedFamiliars, possibles, 0, bestCard, useCrownFamiliar );
any = true;
this.restore( mark );
}
else
{
for ( FamiliarData f : enthronedFamiliars )
{
this.setBjorned( f );
this.tryAccessories( enthronedFamiliars, possibles, 0, bestCard, useCrownFamiliar );
any = true;
this.restore( mark );
}
}
}
else
{
this.tryAccessories( enthronedFamiliars, possibles, 0, bestCard, useCrownFamiliar );
any = true;
this.restore( mark );
}
}
if ( any ) return;
this.equipment[ EquipmentManager.CONTAINER ] = EquipmentRequest.UNEQUIP;
}
this.tryAccessories( enthronedFamiliars, possibles, 0, bestCard, useCrownFamiliar );
this.restore( mark );
}
public void tryAccessories( ArrayList<FamiliarData> enthronedFamiliars, ArrayList[] possibles, int pos, AdventureResult bestCard,
FamiliarData useCrownFamiliar )
throws MaximizerInterruptedException
{
Object mark = this.mark();
int free = 0;
if ( this.equipment[ EquipmentManager.ACCESSORY1 ] == null ) ++free;
if ( this.equipment[ EquipmentManager.ACCESSORY2 ] == null ) ++free;
if ( this.equipment[ EquipmentManager.ACCESSORY3 ] == null ) ++free;
if ( free > 0 )
{
ArrayList possible = possibles[ EquipmentManager.ACCESSORY1 ];
boolean any = false;
for ( ; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
int count = item.getCount();
if ( item.equals( this.equipment[ EquipmentManager.ACCESSORY1 ] ) )
{
--count;
}
if ( item.equals( this.equipment[ EquipmentManager.ACCESSORY2 ] ) )
{
--count;
}
if ( item.equals( this.equipment[ EquipmentManager.ACCESSORY3 ] ) )
{
--count;
}
ArrayList group = ItemDatabase.getFoldGroup( item.getName() );
if ( group != null && this.foldables )
{
String groupName = (String) group.get( 1 );
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
if ( this.equipment[ slot ] != null )
{
ArrayList groupEquipped = ItemDatabase.getFoldGroup( this.equipment[ slot ].getName() );
if ( groupEquipped != null && groupName.equals( (String) groupEquipped.get( 1 ) ) )
{
--count;
}
}
}
}
if ( count <= 0 ) continue;
for ( count = Math.min( free, count ); count > 0; --count )
{
if ( this.equipment[ EquipmentManager.ACCESSORY1 ] == null )
{
this.equipment[ EquipmentManager.ACCESSORY1 ] = item;
}
else if ( this.equipment[ EquipmentManager.ACCESSORY2 ] == null )
{
this.equipment[ EquipmentManager.ACCESSORY2 ] = item;
}
else if ( this.equipment[ EquipmentManager.ACCESSORY3 ] == null )
{
this.equipment[ EquipmentManager.ACCESSORY3 ] = item;
}
else
{
System.out.println( "no room left???" );
break; // no room left - shouldn't happen
}
this.tryAccessories( enthronedFamiliars, possibles, pos + 1, bestCard, useCrownFamiliar );
any = true;
}
this.restore( mark );
}
if ( any ) return;
if ( this.equipment[ EquipmentManager.ACCESSORY1 ] == null )
{
this.equipment[ EquipmentManager.ACCESSORY1 ] = EquipmentRequest.UNEQUIP;
}
if ( this.equipment[ EquipmentManager.ACCESSORY2 ] == null )
{
this.equipment[ EquipmentManager.ACCESSORY2 ] = EquipmentRequest.UNEQUIP;
}
if ( this.equipment[ EquipmentManager.ACCESSORY3 ] == null )
{
this.equipment[ EquipmentManager.ACCESSORY3 ] = EquipmentRequest.UNEQUIP;
}
}
this.trySwap( EquipmentManager.ACCESSORY1, EquipmentManager.ACCESSORY2 );
this.trySwap( EquipmentManager.ACCESSORY2, EquipmentManager.ACCESSORY3 );
this.trySwap( EquipmentManager.ACCESSORY3, EquipmentManager.ACCESSORY1 );
this.tryHats( enthronedFamiliars, possibles, bestCard, useCrownFamiliar );
this.restore( mark );
}
public void tryHats( ArrayList<FamiliarData> enthronedFamiliars, ArrayList[] possibles, AdventureResult bestCard,
FamiliarData useCrownFamiliar )
throws MaximizerInterruptedException
{
Object mark = this.mark();
if ( this.equipment[ EquipmentManager.HAT ] == null )
{
ArrayList possible = possibles[ EquipmentManager.HAT ];
boolean any = false;
for ( int pos = 0; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
int count = item.getCount();
if ( item.equals( this.equipment[ EquipmentManager.FAMILIAR ] ) )
{
--count;
}
ArrayList group = ItemDatabase.getFoldGroup( item.getName() );
if ( group != null && this.foldables )
{
String groupName = (String) group.get( 1 );
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
if ( slot != EquipmentManager.HAT && this.equipment[ slot ] != null )
{
ArrayList groupEquipped = ItemDatabase.getFoldGroup( this.equipment[ slot ].getName() );
if ( groupEquipped != null && groupName.equals( (String) groupEquipped.get( 1 ) ) )
{
--count;
}
}
}
}
if ( count <= 0 ) continue;
this.equipment[ EquipmentManager.HAT ] = item;
if ( item.getItemId() == ItemPool.HATSEAT )
{
if ( useCrownFamiliar != FamiliarData.NO_FAMILIAR )
{
this.setEnthroned( useCrownFamiliar );
this.tryShirts( possibles, bestCard );
any = true;
this.restore( mark );
}
else
{
for ( FamiliarData f : enthronedFamiliars )
{
// Cannot use same familiar for this and Bjorn
if( f != this.getBjorned() )
{
this.setEnthroned( f );
this.tryShirts( possibles, bestCard );
any = true;
this.restore( mark );
}
}
}
}
else
{
this.tryShirts( possibles, bestCard );
any = true;
this.restore( mark );
}
}
if ( any ) return;
this.equipment[ EquipmentManager.HAT ] = EquipmentRequest.UNEQUIP;
}
this.tryShirts( possibles, bestCard );
this.restore( mark );
}
public void tryShirts( ArrayList[] possibles, AdventureResult bestCard )
throws MaximizerInterruptedException
{
Object mark = this.mark();
if ( this.equipment[ EquipmentManager.SHIRT ] == null )
{
boolean any = false;
if ( KoLCharacter.isTorsoAware() )
{
ArrayList possible = possibles[ EquipmentManager.SHIRT ];
for ( int pos = 0; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
int count = item.getCount();
if ( item.equals( this.equipment[ EquipmentManager.FAMILIAR ] ) )
{
--count;
}
ArrayList group = ItemDatabase.getFoldGroup( item.getName() );
if ( group != null && this.foldables )
{
String groupName = (String) group.get( 1 );
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
if ( slot != EquipmentManager.SHIRT && this.equipment[ slot ] != null )
{
ArrayList groupEquipped = ItemDatabase.getFoldGroup( this.equipment[ slot ].getName() );
if ( groupEquipped != null && groupName.equals( (String) groupEquipped.get( 1 ) ) )
{
--count;
}
}
}
}
if ( count <= 0 ) continue;
this.equipment[ EquipmentManager.SHIRT ] = item;
this.tryPants( possibles, bestCard );
any = true;
this.restore( mark );
}
}
if ( any ) return;
this.equipment[ EquipmentManager.SHIRT ] = EquipmentRequest.UNEQUIP;
}
this.tryPants( possibles, bestCard );
this.restore( mark );
}
public void tryPants( ArrayList[] possibles, AdventureResult bestCard )
throws MaximizerInterruptedException
{
Object mark = this.mark();
if ( this.equipment[ EquipmentManager.PANTS ] == null )
{
ArrayList possible = possibles[ EquipmentManager.PANTS ];
boolean any = false;
for ( int pos = 0; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
int count = item.getCount();
if ( item.equals( this.equipment[ EquipmentManager.FAMILIAR ] ) )
{
--count;
}
ArrayList group = ItemDatabase.getFoldGroup( item.getName() );
if ( group != null && this.foldables )
{
String groupName = (String) group.get( 1 );
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
if ( slot != EquipmentManager.PANTS && this.equipment[ slot ] != null )
{
ArrayList groupEquipped = ItemDatabase.getFoldGroup( this.equipment[ slot ].getName() );
if ( groupEquipped != null && groupName.equals( (String) groupEquipped.get( 1 ) ) )
{
--count;
}
}
}
}
if ( count <= 0 ) continue;
this.equipment[ EquipmentManager.PANTS ] = item;
this.trySixguns( possibles, bestCard );
any = true;
this.restore( mark );
}
if ( any ) return;
this.equipment[ EquipmentManager.PANTS ] = EquipmentRequest.UNEQUIP;
}
this.trySixguns( possibles, bestCard );
this.restore( mark );
}
public void trySixguns( ArrayList[] possibles, AdventureResult bestCard )
throws MaximizerInterruptedException
{
Object mark = this.mark();
if ( this.equipment[ EquipmentManager.HOLSTER ] == null )
{
ArrayList possible = possibles[ EquipmentManager.HOLSTER ];
boolean any = false;
for ( int pos = 0; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
int count = item.getCount();
if ( count <= 0 ) continue;
this.equipment[ EquipmentManager.HOLSTER ] = item;
this.tryWeapons( possibles, bestCard );
any = true;
this.restore( mark );
}
if ( any ) return;
this.equipment[ EquipmentManager.HOLSTER ] = EquipmentRequest.UNEQUIP;
}
this.tryWeapons( possibles, bestCard );
this.restore( mark );
}
public void tryWeapons( ArrayList[] possibles, AdventureResult bestCard )
throws MaximizerInterruptedException
{
Object mark = this.mark();
boolean chefstaffable = KoLCharacter.hasSkill( "Spirit of Rigatoni" ) || KoLCharacter.isJarlsberg();
if ( !chefstaffable && KoLCharacter.getClassType().equals( KoLCharacter.SAUCEROR ) )
{
chefstaffable =
this.equipment[ EquipmentManager.ACCESSORY1 ].getItemId() == ItemPool.SPECIAL_SAUCE_GLOVE ||
this.equipment[ EquipmentManager.ACCESSORY2 ].getItemId() == ItemPool.SPECIAL_SAUCE_GLOVE ||
this.equipment[ EquipmentManager.ACCESSORY3 ].getItemId() == ItemPool.SPECIAL_SAUCE_GLOVE;
}
if ( this.equipment[ EquipmentManager.WEAPON ] == null )
{
ArrayList possible = possibles[ EquipmentManager.WEAPON ];
//boolean any = false;
for ( int pos = 0; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
if ( !chefstaffable &&
EquipmentDatabase.getItemType( item.getItemId() ).equals( "chefstaff" ) )
{
continue;
}
int count = item.getCount();
if ( item.equals( this.equipment[ EquipmentManager.OFFHAND ] ) )
{
--count;
}
if ( item.equals( this.equipment[ EquipmentManager.FAMILIAR ] ) )
{
--count;
}
ArrayList group = ItemDatabase.getFoldGroup( item.getName() );
if ( group != null && this.foldables )
{
String groupName = (String) group.get( 1 );
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
if ( slot != EquipmentManager.WEAPON && this.equipment[ slot ] != null )
{
ArrayList groupEquipped = ItemDatabase.getFoldGroup( this.equipment[ slot ].getName() );
if ( groupEquipped != null && groupName.equals( (String) groupEquipped.get( 1 ) ) )
{
--count;
}
}
}
}
if ( count <= 0 ) continue;
this.equipment[ EquipmentManager.WEAPON ] = item;
this.tryOffhands( possibles, bestCard );
//any = true;
this.restore( mark );
}
// if ( any && <no unarmed items in shortlists> ) return;
if ( Maximizer.eval.melee < -1 || Maximizer.eval.melee > 1 )
{
return;
}
this.equipment[ EquipmentManager.WEAPON ] = EquipmentRequest.UNEQUIP;
}
else if ( !chefstaffable &&
EquipmentDatabase.getItemType( this.equipment[ EquipmentManager.WEAPON ].getItemId() ).equals( "chefstaff" ) )
{
return;
}
this.tryOffhands( possibles, bestCard );
this.restore( mark );
}
public void tryOffhands( ArrayList[] possibles, AdventureResult bestCard )
throws MaximizerInterruptedException
{
Object mark = this.mark();
int weapon = this.equipment[ EquipmentManager.WEAPON ].getItemId();
if ( EquipmentDatabase.getHands( weapon ) > 1 )
{
this.equipment[ EquipmentManager.OFFHAND ] = EquipmentRequest.UNEQUIP;
}
if ( this.equipment[ EquipmentManager.OFFHAND ] == null )
{
ArrayList possible;
WeaponType weaponType = WeaponType.NONE;
if ( KoLCharacter.hasSkill( "Double-Fisted Skull Smashing" ) )
{
weaponType = EquipmentDatabase.getWeaponType( weapon );
}
switch ( weaponType )
{
case MELEE:
possible = possibles[ Evaluator.OFFHAND_MELEE ];
break;
case RANGED:
possible = possibles[ Evaluator.OFFHAND_RANGED ];
break;
default:
possible = possibles[ EquipmentManager.OFFHAND ];
}
boolean any = false;
for ( int pos = 0; pos < possible.size(); ++pos )
{
AdventureResult item = (AdventureResult) possible.get( pos );
int count = item.getCount();
if ( item.equals( this.equipment[ EquipmentManager.WEAPON ] ) )
{
--count;
}
if ( item.equals( this.equipment[ EquipmentManager.FAMILIAR ] ) )
{
--count;
}
ArrayList group = ItemDatabase.getFoldGroup( item.getName() );
if ( group != null && this.foldables )
{
String groupName = (String) group.get( 1 );
for ( int slot = 0; slot < EquipmentManager.ALL_SLOTS; ++slot )
{
if ( slot != EquipmentManager.OFFHAND && this.equipment[ slot ] != null )
{
ArrayList groupEquipped = ItemDatabase.getFoldGroup( this.equipment[ slot ].getName() );
if ( groupEquipped != null && groupName.equals( (String) groupEquipped.get( 1 ) ) )
{
--count;
}
}
}
}
if ( count <= 0 ) continue;
if ( item.getItemId() == ItemPool.CARD_SLEEVE )
{
this.equipment[ EquipmentManager.CARDSLEEVE ] = bestCard;
}
this.equipment[ EquipmentManager.OFFHAND ] = item;
this.tryOffhands( possibles, bestCard );
any = true;
this.restore( mark );
}
if ( any && weapon > 0 ) return;
this.equipment[ EquipmentManager.OFFHAND ] = EquipmentRequest.UNEQUIP;
}
// doit
this.calculated = false;
this.scored = false;
this.tiebreakered = false;
if ( this.compareTo( Maximizer.best ) > 0 )
{
Maximizer.best = (MaximizerSpeculation) this.clone();
}
Maximizer.bestChecked++;
long t = System.currentTimeMillis();
if ( t > Maximizer.bestUpdate )
{
MaximizerSpeculation.showProgress();
Maximizer.bestUpdate = t + 5000;
}
this.restore( mark );
if ( !KoLmafia.permitsContinue() )
{
throw new MaximizerInterruptedException();
}
if ( this.exceeded )
{
throw new MaximizerExceededException();
}
long comboLimit = Preferences.getLong( "maximizerCombinationLimit" );
if ( comboLimit != 0 && Maximizer.bestChecked >= comboLimit )
{
throw new MaximizerLimitException();
}
}
private static int getMutex( AdventureResult item )
{
Modifiers mods = Modifiers.getItemModifiers( item.getItemId() );
if ( mods == null )
{
return 0;
}
return mods.getRawBitmap( Modifiers.MUTEX );
}
private void trySwap( int slot1, int slot2 )
{
// If we are suggesting an accessory that's already being worn,
// make sure we suggest the same slot (to minimize server hits).
AdventureResult item1, item2, eq1, eq2;
item1 = this.equipment[ slot1 ];
if ( item1 == null ) item1 = EquipmentRequest.UNEQUIP;
eq1 = EquipmentManager.getEquipment( slot1 );
if ( eq1.equals( item1 ) ) return;
item2 = this.equipment[ slot2 ];
if ( item2 == null ) item2 = EquipmentRequest.UNEQUIP;
eq2 = EquipmentManager.getEquipment( slot2 );
if ( eq2.equals( item2 ) ) return;
// The same thing applies to mutually exclusive accessories -
// putting the new one in an earlier slot would cause an error
// when the equipment is being changed.
int imutex1, imutex2, emutex1, emutex2;
imutex1 = getMutex( item1 );
emutex1 = getMutex( eq1 );
if ( (imutex1 & emutex1) != 0 ) return;
imutex2 = getMutex( item2 );
emutex2 = getMutex( eq2 );
if ( (imutex2 & emutex2) != 0 ) return;
if ( eq1.equals( item2 ) || eq2.equals( item1 ) ||
(imutex1 & emutex2) != 0 || (imutex2 & emutex1) != 0 )
{
this.equipment[ slot1 ] = item2;
this.equipment[ slot2 ] = item1;
}
}
public static void showProgress()
{
StringBuilder msg = new StringBuilder();
msg.append( Maximizer.bestChecked );
msg.append( " combinations checked, best score " );
double score = Maximizer.best.getScore();
msg.append( KoLConstants.FLOAT_FORMAT.format( score ) );
if ( Maximizer.best.failed )
{
msg.append( " (FAIL)" );
}
//if ( MaximizerFrame.best.tiebreakered )
//{
// msg = msg + " / " + MaximizerFrame.best.getTiebreaker() + " / " +
// MaximizerFrame.best.simplicity;
//}
KoLmafia.updateDisplay( msg.toString() );
}
}