From 94c36b571aa03452c98b7358af0e17de07509437 Mon Sep 17 00:00:00 2001 From: Agetian Date: Tue, 19 Mar 2013 02:40:58 +0000 Subject: [PATCH] - Reintegrate merge: AIPersonalities --> trunk. - The primary API for AI personalities is now in place. AI properties are implemented in forge.game.ai.AiProps and are retrieved via a call to one of AiController.getProperty, AiController.getIntProperty, or AiController.getBooleanProperty depending on the type of property (AI controller can be retrieved via a getAi() call on the AI player object). Each property can be assigned on a per-profile basis in res/ai/*.ai profile files. A profile can be selected in the game properties. For an example implementation of a property, see AI_MULLIGAN_THRESHOLD implemented in AiProps and retrieved in ComputerUtil. - Currently there is one property only, but more are on the way. Everyone is also free to expand as necessary. Specific AI profiles for specific quest opponents will be implemented at a later date. --- .gitattributes | 3 + res/ai/Default.ai | 1 + src/main/java/forge/control/FControl.java | 4 + src/main/java/forge/game/MatchController.java | 21 ++ src/main/java/forge/game/ai/AiController.java | 12 ++ .../java/forge/game/ai/AiProfileUtil.java | 184 ++++++++++++++++++ src/main/java/forge/game/ai/AiProps.java | 41 ++++ src/main/java/forge/game/ai/ComputerUtil.java | 4 +- .../java/forge/game/player/LobbyPlayer.java | 14 ++ src/main/java/forge/game/player/Player.java | 1 - .../home/settings/CSubmenuPreferences.java | 35 ++++ .../home/settings/VSubmenuPreferences.java | 33 +++- .../forge/properties/ForgePreferences.java | 2 + 13 files changed, 350 insertions(+), 5 deletions(-) create mode 100644 res/ai/Default.ai create mode 100644 src/main/java/forge/game/ai/AiProfileUtil.java create mode 100644 src/main/java/forge/game/ai/AiProps.java diff --git a/.gitattributes b/.gitattributes index 923154272da..3f33ee2791e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,6 +10,7 @@ /LICENSE.txt svneol=native#text/plain /README.txt svneol=native#text/plain /pom.xml svneol=native#text/xml +res/ai/Default.ai -text res/blockdata/blocks.txt svneol=native#text/plain res/blockdata/boosters.txt -text res/blockdata/fantasyblocks.txt -text @@ -14132,6 +14133,8 @@ src/main/java/forge/game/ai/AiAttackController.java svneol=native#text/plain src/main/java/forge/game/ai/AiController.java svneol=native#text/plain src/main/java/forge/game/ai/AiInputBlock.java -text src/main/java/forge/game/ai/AiInputCommon.java svneol=native#text/plain +src/main/java/forge/game/ai/AiProfileUtil.java -text +src/main/java/forge/game/ai/AiProps.java -text src/main/java/forge/game/ai/ComputerUtil.java svneol=native#text/plain src/main/java/forge/game/ai/ComputerUtilBlock.java svneol=native#text/plain src/main/java/forge/game/ai/ComputerUtilCard.java -text diff --git a/res/ai/Default.ai b/res/ai/Default.ai new file mode 100644 index 00000000000..ff5239c5d8b --- /dev/null +++ b/res/ai/Default.ai @@ -0,0 +1 @@ +AI_MULLIGAN_THRESHOLD=5 diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index c4310a85786..fffeba57872 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -34,6 +34,7 @@ import javax.swing.WindowConstants; import forge.Singletons; import forge.control.KeyboardShortcuts.Shortcut; +import forge.game.ai.AiProfileUtil; import forge.game.player.Player; import forge.gui.SOverlayUtils; import forge.gui.deckeditor.CDeckEditorUI; @@ -162,6 +163,9 @@ public enum FControl { Singletons.getModel().getQuest().load(QuestDataIO.loadData(data)); } + // Preload AI profiles + AiProfileUtil.loadAllProfiles(); + // Handles resizing in null layouts of layers in JLayeredPane. Singletons.getView().getFrame().addComponentListener(new ComponentAdapter() { @Override diff --git a/src/main/java/forge/game/MatchController.java b/src/main/java/forge/game/MatchController.java index e0dd57a45dc..73967a75d90 100644 --- a/src/main/java/forge/game/MatchController.java +++ b/src/main/java/forge/game/MatchController.java @@ -13,6 +13,7 @@ import forge.control.FControl; import forge.control.input.InputControl; import forge.deck.Deck; import forge.error.BugReporter; +import forge.game.ai.AiProfileUtil; import forge.game.event.DuelOutcomeEvent; import forge.game.player.LobbyPlayer; import forge.game.player.Player; @@ -137,6 +138,26 @@ public class MatchController { startConditions.put(p, players.get(p.getLobbyPlayer())); } + // Set the current AI profile. + for (Player p : currentGame.getPlayers()) { + if (p.getType() == PlayerType.COMPUTER) { + if (Singletons.getModel().getPreferences().getPref(FPref.UI_CURRENT_AI_PROFILE).equals(AiProfileUtil.AI_PROFILE_RANDOM_DUEL)) { + String randomProfile = AiProfileUtil.getRandomProfile(); + p.getLobbyPlayer().setAiProfile(randomProfile); + } else if (Singletons.getModel().getPreferences().getPref(FPref.UI_CURRENT_AI_PROFILE).equals(AiProfileUtil.AI_PROFILE_RANDOM_MATCH)) { + if (this.getPlayedGames().isEmpty()) { + String randomProfile = AiProfileUtil.getRandomProfile(); + p.getLobbyPlayer().setAiProfile(randomProfile); + } + } else { + // TODO: implement specific AI profiles for quest mode. + String profile = Singletons.getModel().getPreferences().getPref(FPref.UI_CURRENT_AI_PROFILE); + p.getLobbyPlayer().setAiProfile(profile); + } + System.out.println(String.format("AI profile %s was chosen for the lobby player %s.", p.getLobbyPlayer().getAiProfile(), p.getLobbyPlayer().getName())); + } + } + try { Player localHuman = Aggregates.firstFieldEquals(currentGame.getPlayers(), Player.Accessors.FN_GET_TYPE, PlayerType.HUMAN); FControl.SINGLETON_INSTANCE.setPlayer(localHuman); diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index 3db947596de..dbba372c178 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -719,5 +719,17 @@ public class AiController { } return null; } + + public String getProperty(AiProps propName) { + return AiProfileUtil.getAIProp(getPlayer().getLobbyPlayer(), propName); + } + + public int getIntProperty(AiProps propName) { + return Integer.parseInt(AiProfileUtil.getAIProp(getPlayer().getLobbyPlayer(), propName)); + } + + public boolean getBooleanProperty(AiProps propName) { + return Boolean.parseBoolean(AiProfileUtil.getAIProp(getPlayer().getLobbyPlayer(), propName)); + } } diff --git a/src/main/java/forge/game/ai/AiProfileUtil.java b/src/main/java/forge/game/ai/AiProfileUtil.java new file mode 100644 index 00000000000..3db912f0a0a --- /dev/null +++ b/src/main/java/forge/game/ai/AiProfileUtil.java @@ -0,0 +1,184 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2013 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game.ai; + +import forge.Singletons; +import forge.game.player.LobbyPlayer; +import forge.util.Aggregates; +import forge.util.FileUtil; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; + +/** + * Holds default AI personality profile values in an enum. + * Loads profile from the given text file when setProfile is called. + * If a requested value is not loaded from a profile, default is returned. + * + * @author Forge + * @version $Id: AIProfile.java 20169 2013-03-08 08:24:17Z Agetian $ + */ +public class AiProfileUtil { + private static Map> loadedProfiles = new HashMap>(); + + private static final String AI_PROFILE_DIR = "res/ai"; + private static final String AI_PROFILE_EXT = ".ai"; + + public static final String AI_PROFILE_RANDOM_MATCH = "* Random (Match) *"; + public static final String AI_PROFILE_RANDOM_DUEL = "* Random (Duel) *"; + + /** Builds an AI profile file name with full relative + * path based on the profile name. + * @param profileName the name of the profile. + * @return the full relative path and file name for the given profile. + */ + private static String buildFileName(final String profileName) { + return String.format("%s/%s%s", AI_PROFILE_DIR, profileName, AI_PROFILE_EXT); + } + + /** + * Load all profiles + */ + public static final void loadAllProfiles() { + loadedProfiles.clear(); + ArrayList availableProfiles = getAvailableProfiles(); + for (String profile : availableProfiles) { + loadedProfiles.put(profile, loadProfile(profile)); + } + } + + /** + * Load a single profile. + * @param profileName a profile to load. + */ + private static final Map loadProfile(final String profileName) { + Map profileMap = new HashMap(); + + List lines = FileUtil.readFile(buildFileName(profileName)); + for (String line : lines) { + + if (line.startsWith("#") || (line.length() == 0)) { + continue; + } + + final String[] split = line.split("="); + + if (split.length == 2) { + profileMap.put(AiProps.valueOf(split[0]), split[1]); + } else if (split.length == 1 && line.endsWith("=")) { + profileMap.put(AiProps.valueOf(split[0]), ""); + } + } + + return profileMap; + } + + /** + * Returns an AI property value for the current profile. + * + * @param fp0 an AI property. + * @return String + */ + public static String getAIProp(final LobbyPlayer p, final AiProps fp0) { + String val = null; + String profile = p.getAiProfile(); + + if (loadedProfiles.get(profile) != null) { + val = loadedProfiles.get(profile).get(fp0); + } + if (val == null) { val = fp0.getDefault(); } + + return val; + } + + /** + * Returns an array of strings containing all available profiles. + * @return ArrayList - an array of strings containing all + * available profiles. + */ + public static ArrayList getAvailableProfiles() + { + final ArrayList availableProfiles = new ArrayList(); + + final File dir = new File(AI_PROFILE_DIR); + final String[] children = dir.list(); + if (children == null) { + System.err.println("AIProfile > can't find AI profile directory!"); + } else { + for (int i = 0; i < children.length; i++) { + if (children[i].endsWith(AI_PROFILE_EXT)) { + availableProfiles.add(children[i].substring(0, children[i].length() - AI_PROFILE_EXT.length())); + } + } + } + + return availableProfiles; + } + + /** + * Returns an array of strings containing all available profiles including + * the special "Random" profiles. + * @return ArrayList - an array list of strings containing all + * available profiles including special random profile tags. + */ + public static ArrayList getProfilesDisplayList() { + final ArrayList availableProfiles = new ArrayList(); + availableProfiles.add(AI_PROFILE_RANDOM_MATCH); + availableProfiles.add(AI_PROFILE_RANDOM_DUEL); + availableProfiles.addAll(getAvailableProfiles()); + + return availableProfiles; + } + + /** + * Returns a random personality from the currently available ones. + * @return String - a string containing a random profile from all the + * currently available ones. + */ + public static String getRandomProfile() { + return Aggregates.random(getAvailableProfiles()); + } + + /** + * Simple class test facility for AiProfileUtil. + */ + public static void selfTest() { + final LobbyPlayer activePlayer = Singletons.getControl().getPlayer().getLobbyPlayer(); + System.out.println(String.format("Current profile = %s", activePlayer.getAiProfile())); + ArrayList profiles = getAvailableProfiles(); + System.out.println(String.format("Available profiles: %s", profiles)); + if (profiles.size() > 0) { + System.out.println(String.format("Loading all profiles...")); + loadAllProfiles(); + System.out.println(String.format("Setting profile %s...", profiles.get(0))); + activePlayer.setAiProfile(profiles.get(0)); + for (AiProps property : AiProps.values()) { + System.out.println(String.format("%s = %s", property, getAIProp(activePlayer, property))); + } + String randomProfile = getRandomProfile(); + System.out.println(String.format("Loading random profile %s...", randomProfile)); + activePlayer.setAiProfile(randomProfile); + for (AiProps property : AiProps.values()) { + System.out.println(String.format("%s = %s", property, getAIProp(activePlayer, property))); + } + } + } +} diff --git a/src/main/java/forge/game/ai/AiProps.java b/src/main/java/forge/game/ai/AiProps.java new file mode 100644 index 00000000000..3484e5b70a4 --- /dev/null +++ b/src/main/java/forge/game/ai/AiProps.java @@ -0,0 +1,41 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2013 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game.ai; + +/** + * AI personality profile settings identifiers, and their default values. + * When this class is instantiated, these enum values are used + * in a map that is populated with the current AI profile settings + * from the text file. + */ +public enum AiProps { /** */ + AI_MULLIGAN_THRESHOLD ("5"); /** */ + + private final String strDefaultVal; + + /** @param s0   {@link java.lang.String} */ + AiProps(final String s0) { + this.strDefaultVal = s0; + } + + /** @return {@link java.lang.String} */ + public String getDefault() { + return strDefaultVal; + } +} + diff --git a/src/main/java/forge/game/ai/ComputerUtil.java b/src/main/java/forge/game/ai/ComputerUtil.java index ae723cd7071..fa0f93d9635 100644 --- a/src/main/java/forge/game/ai/ComputerUtil.java +++ b/src/main/java/forge/game/ai/ComputerUtil.java @@ -1202,11 +1202,9 @@ public class ComputerUtil { // Computer mulligans if there are no cards with converted mana cost of // 0 in its hand public static boolean wantMulligan(AIPlayer ai) { - final int AI_MULLIGAN_THRESHOLD = 5; - final List handList = ai.getCardsIn(ZoneType.Hand); final boolean hasLittleCmc0Cards = CardLists.getValidCards(handList, "Card.cmcEQ0", ai, null).size() < 2; - return (handList.size() > AI_MULLIGAN_THRESHOLD) && hasLittleCmc0Cards; + return (handList.size() > ai.getAi().getIntProperty(AiProps.AI_MULLIGAN_THRESHOLD)) && hasLittleCmc0Cards; } diff --git a/src/main/java/forge/game/player/LobbyPlayer.java b/src/main/java/forge/game/player/LobbyPlayer.java index 2efb0d9281d..28adde731b1 100644 --- a/src/main/java/forge/game/player/LobbyPlayer.java +++ b/src/main/java/forge/game/player/LobbyPlayer.java @@ -1,5 +1,8 @@ package forge.game.player; +import forge.game.ai.AiProfileUtil; +import forge.game.ai.AiProps; + /** * This means a player's part unchanged for all games. * @@ -17,6 +20,9 @@ public class LobbyPlayer implements IHasIcon { protected String imageKey; private int avatarIndex = -1; + /** The AI profile. */ + private String aiProfile = ""; + public LobbyPlayer(PlayerType type, String name) { this.type = type; @@ -75,4 +81,12 @@ public class LobbyPlayer implements IHasIcon { public void setAvatarIndex(int avatarIndex) { this.avatarIndex = avatarIndex; } + + public void setAiProfile(String profileName) { + aiProfile = profileName; + } + + public String getAiProfile() { + return aiProfile; + } } diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 5da39b7bc01..a595e1c7445 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -164,7 +164,6 @@ public abstract class Player extends GameEntity implements Comparable { ZoneType.Library, ZoneType.Graveyard, ZoneType.Hand, ZoneType.Exile, ZoneType.Command, ZoneType.Ante, ZoneType.Sideboard)); - protected final LobbyPlayer lobbyPlayer; protected final GameState game; diff --git a/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java b/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java index 449b438fef1..988ee96cc89 100644 --- a/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java +++ b/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java @@ -13,10 +13,12 @@ import forge.Command; import forge.Constant.Preferences; import forge.Singletons; import forge.control.RestartUtil; +import forge.game.ai.AiProfileUtil; import forge.gui.framework.ICDoc; import forge.gui.toolbox.FSkin; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; +import java.util.ArrayList; /** * Controls the preferences submenu in the home UI. @@ -44,6 +46,13 @@ public enum CSubmenuPreferences implements ICDoc { } }); + view.getLstChooseAIProfile().addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(final MouseEvent e) { + updateAIProfile(); + } + }); + view.getCbAnte().addItemListener(new ItemListener() { @Override public void itemStateChanged(final ItemEvent arg0) { @@ -187,6 +196,7 @@ public enum CSubmenuPreferences implements ICDoc { final VSubmenuPreferences view = VSubmenuPreferences.SINGLETON_INSTANCE; final ForgePreferences prefs = Singletons.getModel().getPreferences(); updateSkinNames(); + updateAIProfiles(); view.getCbRemoveSmall().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_NOSMALL)); view.getCbSingletons().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_SINGLETONS)); @@ -226,6 +236,21 @@ public enum CSubmenuPreferences implements ICDoc { view.getLstChooseSkin().ensureIndexIsVisible(view.getLstChooseSkin().getSelectedIndex()); } + private void updateAIProfiles() { + final VSubmenuPreferences view = VSubmenuPreferences.SINGLETON_INSTANCE; + final ArrayList profileNames = AiProfileUtil.getProfilesDisplayList(); + final String currentName = Singletons.getModel().getPreferences().getPref(FPref.UI_CURRENT_AI_PROFILE); + int currentIndex = 0; + + for (int i = 0; i < profileNames.size(); i++) { + if (currentName.equalsIgnoreCase(profileNames.get(i))) { currentIndex = i; } + } + + view.getLstChooseAIProfile().setListData(profileNames.toArray()); + view.getLstChooseAIProfile().setSelectedIndex(currentIndex); + view.getLstChooseAIProfile().ensureIndexIsVisible(view.getLstChooseAIProfile().getSelectedIndex()); + } + @SuppressWarnings("serial") private void updateSkin() { final VSubmenuPreferences view = VSubmenuPreferences.SINGLETON_INSTANCE; @@ -243,6 +268,16 @@ public enum CSubmenuPreferences implements ICDoc { prefs.save(); } + @SuppressWarnings("serial") + private void updateAIProfile() { + final VSubmenuPreferences view = VSubmenuPreferences.SINGLETON_INSTANCE; + final String name = view.getLstChooseAIProfile().getSelectedValue().toString(); + final ForgePreferences prefs = Singletons.getModel().getPreferences(); + if (name.equals(prefs.getPref(FPref.UI_CURRENT_AI_PROFILE))) { return; } + + prefs.setPref(FPref.UI_CURRENT_AI_PROFILE, name); + prefs.save(); + } /* (non-Javadoc) * @see forge.gui.framework.ICDoc#getCommandOnSelect() */ diff --git a/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java b/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java index 925ab3c3348..d8306dd4995 100644 --- a/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java +++ b/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java @@ -64,7 +64,7 @@ public enum VSubmenuPreferences implements IVSubmenu { .hoverable(true).text("Reset to defaults").build(); private final FLabel lblTitleSkin = new FLabel.Builder() - .text("Choose Skin").fontStyle(Font.BOLD).fontSize(14).build(); + .text("Choose Skin").fontStyle(Font.BOLD).fontSize(14).build(); private final JList lstChooseSkin = new FList(); private final FLabel lblChooseSkin = new FLabel.Builder().fontSize(12).fontStyle(Font.ITALIC) @@ -72,6 +72,15 @@ public enum VSubmenuPreferences implements IVSubmenu { .fontAlign(SwingConstants.LEFT).build(); private final JScrollPane scrChooseSkin = new FScrollPane(lstChooseSkin); + private final FLabel lblTitleAIProfile = new FLabel.Builder() + .text("Choose AI Personality").fontStyle(Font.BOLD).fontSize(14).build(); + + private final JList lstChooseAIProfile = new FList(); + private final FLabel lblChooseAIProfile = new FLabel.Builder().fontSize(12).fontStyle(Font.ITALIC) + .text("AI Opponent Personality.") + .fontAlign(SwingConstants.LEFT).build(); + private final JScrollPane scrChooseAIProfile = new FScrollPane(lstChooseAIProfile); + private final JCheckBox cbRemoveSmall = new OptionsCheckBox("Remove Small Creatures"); private final JCheckBox cbSingletons = new OptionsCheckBox("Singleton Mode"); private final JCheckBox cbRemoveArtifacts = new OptionsCheckBox("Remove Artifacts"); @@ -134,6 +143,13 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbEnforceDeckLegality, regularConstraints); pnlPrefs.add(new NoteLabel("Enforces deck legality relevant to each environment (minimum deck sizes, max card count etc)"), regularConstraints); + // AI Personality Profile Options + pnlPrefs.add(new SectionLabel("AI Options"), sectionConstraints); + + pnlPrefs.add(lblTitleAIProfile, regularConstraints); + pnlPrefs.add(lblChooseAIProfile, regularConstraints); + pnlPrefs.add(scrChooseAIProfile, "h 200px!, w 200px!, gap 10% 0 0 2%, wrap"); + // Graphic Options pnlPrefs.add(new SectionLabel("Graphic Options"), sectionConstraints); @@ -355,6 +371,21 @@ public enum VSubmenuPreferences implements IVSubmenu { return scrChooseSkin; } + /** @return {@link javax.swing.JList} */ + public final JList getLstChooseAIProfile() { + return lstChooseAIProfile; + } + + /** @return {@link forge.gui.toolbox.FLabel} */ + public final FLabel getLblChooseAIProfile() { + return lblChooseAIProfile; + } + + /** @return {@link javax.swing.JScrollPane} */ + public final JScrollPane getScrChooseAIProfile() { + return scrChooseAIProfile; + } + /** @return {@link javax.swing.JCheckBox} */ public final JCheckBox getCbRemoveSmall() { return cbRemoveSmall; diff --git a/src/main/java/forge/properties/ForgePreferences.java b/src/main/java/forge/properties/ForgePreferences.java index 0e5a4e4bafe..8a951c5f44d 100644 --- a/src/main/java/forge/properties/ForgePreferences.java +++ b/src/main/java/forge/properties/ForgePreferences.java @@ -21,6 +21,7 @@ import java.util.List; import forge.Constant; import forge.Constant.Preferences; +import forge.game.ai.AiProfileUtil; import forge.gui.home.EMenuItem; import forge.gui.match.VMatchUI; import forge.gui.match.nonsingleton.VField; @@ -50,6 +51,7 @@ public class ForgePreferences extends PreferencesStore { UI_TARGETING_OVERLAY ("false"), UI_ENABLE_SOUNDS ("true"), UI_RANDOM_CARD_ART ("false"), + UI_CURRENT_AI_PROFILE (AiProfileUtil.AI_PROFILE_RANDOM_MATCH), /** */ SUBMENU_CURRENTMENU (EMenuItem.CONSTRUCTED.toString()), SUBMENU_SANCTIONED ("false"),