puzzle solver: add lightbox solver
This commit is contained in:
@@ -25,16 +25,40 @@
|
||||
*/
|
||||
package net.runelite.client.plugins.puzzlesolver;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Provides;
|
||||
import java.util.Arrays;
|
||||
import javax.inject.Inject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.MenuOptionClicked;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_A;
|
||||
import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_B;
|
||||
import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_C;
|
||||
import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_D;
|
||||
import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_E;
|
||||
import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_F;
|
||||
import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_G;
|
||||
import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_H;
|
||||
import static net.runelite.api.widgets.WidgetInfo.TO_GROUP;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.plugins.puzzlesolver.lightbox.Combination;
|
||||
import net.runelite.client.plugins.puzzlesolver.lightbox.LightBox;
|
||||
import net.runelite.client.plugins.puzzlesolver.lightbox.LightboxSolution;
|
||||
import net.runelite.client.plugins.puzzlesolver.lightbox.LightboxSolver;
|
||||
import net.runelite.client.plugins.puzzlesolver.lightbox.LightboxState;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Puzzle Solver"
|
||||
)
|
||||
@Slf4j
|
||||
public class PuzzleSolverPlugin extends Plugin
|
||||
{
|
||||
@Inject
|
||||
@@ -43,11 +67,13 @@ public class PuzzleSolverPlugin extends Plugin
|
||||
@Inject
|
||||
private PuzzleSolverOverlay overlay;
|
||||
|
||||
@Provides
|
||||
PuzzleSolverConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(PuzzleSolverConfig.class);
|
||||
}
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
private LightboxState lightbox;
|
||||
private LightboxState[] changes = new LightboxState[LightBox.COMBINATIONS_POWER];
|
||||
private Combination lastClick;
|
||||
private boolean lastClickInvalid;
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
@@ -60,4 +86,156 @@ public class PuzzleSolverPlugin extends Plugin
|
||||
{
|
||||
overlayManager.remove(overlay);
|
||||
}
|
||||
|
||||
@Provides
|
||||
PuzzleSolverConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(PuzzleSolverConfig.class);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onWidgetClicked(MenuOptionClicked menuOptionClicked)
|
||||
{
|
||||
int widgetId = menuOptionClicked.getWidgetId();
|
||||
if (TO_GROUP(widgetId) != WidgetID.LIGHT_BOX_GROUP_ID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Combination combination;
|
||||
if (widgetId == LIGHT_BOX_BUTTON_A.getId())
|
||||
{
|
||||
combination = Combination.A;
|
||||
}
|
||||
else if (widgetId == LIGHT_BOX_BUTTON_B.getId())
|
||||
{
|
||||
combination = Combination.B;
|
||||
}
|
||||
else if (widgetId == LIGHT_BOX_BUTTON_C.getId())
|
||||
{
|
||||
combination = Combination.C;
|
||||
}
|
||||
else if (widgetId == LIGHT_BOX_BUTTON_D.getId())
|
||||
{
|
||||
combination = Combination.D;
|
||||
}
|
||||
else if (widgetId == LIGHT_BOX_BUTTON_E.getId())
|
||||
{
|
||||
combination = Combination.E;
|
||||
}
|
||||
else if (widgetId == LIGHT_BOX_BUTTON_F.getId())
|
||||
{
|
||||
combination = Combination.F;
|
||||
}
|
||||
else if (widgetId == LIGHT_BOX_BUTTON_G.getId())
|
||||
{
|
||||
combination = Combination.G;
|
||||
}
|
||||
else if (widgetId == LIGHT_BOX_BUTTON_H.getId())
|
||||
{
|
||||
combination = Combination.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastClick != null)
|
||||
{
|
||||
lastClickInvalid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastClick = combination;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
Widget lightboxWidget = client.getWidget(WidgetInfo.LIGHT_BOX_CONTENTS);
|
||||
if (lightboxWidget == null)
|
||||
{
|
||||
if (lightbox != null)
|
||||
{
|
||||
lastClick = null;
|
||||
lastClickInvalid = false;
|
||||
lightbox = null;
|
||||
Arrays.fill(changes, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get current state from widget
|
||||
LightboxState lightboxState = new LightboxState();
|
||||
int index = 0;
|
||||
for (Widget light : lightboxWidget.getDynamicChildren())
|
||||
{
|
||||
boolean lit = light.getItemId() == LightBox.LIGHT_BULB_ON;
|
||||
lightboxState.setState(index / LightBox.WIDTH, index % LightBox.HEIGHT, lit);
|
||||
index++;
|
||||
}
|
||||
|
||||
if (lightboxState.equals(lightbox))
|
||||
{
|
||||
return; // no change
|
||||
}
|
||||
|
||||
log.debug("Lightbox changed!");
|
||||
|
||||
LightboxState prev = lightbox;
|
||||
lightbox = lightboxState;
|
||||
|
||||
if (lastClick == null || lastClickInvalid)
|
||||
{
|
||||
lastClick = null;
|
||||
lastClickInvalid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
LightboxState diff = lightboxState.diff(prev);
|
||||
changes[lastClick.ordinal()] = diff;
|
||||
|
||||
log.debug("Recorded diff for {}", lastClick);
|
||||
lastClick = null;
|
||||
|
||||
// try to solve
|
||||
LightboxSolver solver = new LightboxSolver();
|
||||
solver.setInitial(lightbox);
|
||||
int idx = 0;
|
||||
for (LightboxState state : changes)
|
||||
{
|
||||
if (state != null)
|
||||
{
|
||||
Combination combination = Combination.values()[idx];
|
||||
solver.setSwitchChange(combination, state);
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
|
||||
LightboxSolution solution = solver.solve();
|
||||
if (solution != null)
|
||||
{
|
||||
log.debug("Got solution: {}", solution);
|
||||
}
|
||||
|
||||
// Set solution to title
|
||||
Widget lightbox = client.getWidget(WidgetInfo.LIGHT_BOX);
|
||||
if (lightbox != null)
|
||||
{
|
||||
Widget title = lightbox.getChild(1);
|
||||
if (solution != null && solution.numMoves() > 0)
|
||||
{
|
||||
title.setText("Light box - Solution: " + solution);
|
||||
}
|
||||
else if (solution != null)
|
||||
{
|
||||
title.setText("Light box - Solution: solved!");
|
||||
}
|
||||
else
|
||||
{
|
||||
title.setText("Light box - Solution: unknown");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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.
|
||||
*
|
||||
* 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.runelite.client.plugins.puzzlesolver.lightbox;
|
||||
|
||||
public enum Combination
|
||||
{
|
||||
A, B, C, D, E, F, G, H;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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.
|
||||
*
|
||||
* 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.runelite.client.plugins.puzzlesolver.lightbox;
|
||||
|
||||
public class LightBox
|
||||
{
|
||||
public static final int WIDTH = 5;
|
||||
public static final int HEIGHT = 5;
|
||||
public static final int COMBINATIONS_POWER = 8;
|
||||
public static final int LIGHT_BULB_ON = 20357;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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.
|
||||
*
|
||||
* 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.runelite.client.plugins.puzzlesolver.lightbox;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class LightboxSolution
|
||||
{
|
||||
private int solution;
|
||||
|
||||
public void flip(Combination c)
|
||||
{
|
||||
solution ^= (1 << c.ordinal());
|
||||
}
|
||||
|
||||
public int numMoves()
|
||||
{
|
||||
int count = 0;
|
||||
int cur = solution;
|
||||
for (int i = 0; i < Combination.values().length; ++i)
|
||||
{
|
||||
count += cur & 1;
|
||||
cur >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (Combination combination : Combination.values())
|
||||
{
|
||||
if (((solution >>> combination.ordinal()) & 1) != 0)
|
||||
{
|
||||
stringBuilder.append(combination.name());
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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.
|
||||
*
|
||||
* 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.runelite.client.plugins.puzzlesolver.lightbox;
|
||||
|
||||
public class LightboxSolver
|
||||
{
|
||||
private LightboxState initial;
|
||||
private final LightboxState[] switches = new LightboxState[LightBox.COMBINATIONS_POWER];
|
||||
|
||||
static boolean isBitSet(int num, int bit)
|
||||
{
|
||||
return ((num >>> bit) & 1) != 0;
|
||||
}
|
||||
|
||||
private static boolean isSolved(LightboxState s)
|
||||
{
|
||||
for (int i = 0; i < LightBox.WIDTH; ++i)
|
||||
{
|
||||
for (int j = 0; j < LightBox.HEIGHT; ++j)
|
||||
{
|
||||
if (!s.getState(i, j))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public LightboxSolution solve()
|
||||
{
|
||||
LightboxSolution solution = null;
|
||||
outer:
|
||||
for (int i = 0; i < Math.pow(2, LightBox.COMBINATIONS_POWER); ++i)
|
||||
{
|
||||
LightboxState s = initial;
|
||||
|
||||
for (int bit = 0; bit < LightBox.COMBINATIONS_POWER; ++bit)
|
||||
{
|
||||
if (isBitSet(i, bit))
|
||||
{
|
||||
// this switch is unknown, so this can't be a valid answer
|
||||
if (switches[bit] == null)
|
||||
{
|
||||
continue outer;
|
||||
}
|
||||
|
||||
s = s.diff(switches[bit]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isSolved(s))
|
||||
{
|
||||
LightboxSolution sol = new LightboxSolution(i);
|
||||
if (solution == null || sol.numMoves() < solution.numMoves())
|
||||
{
|
||||
solution = sol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
public void setInitial(LightboxState initial)
|
||||
{
|
||||
this.initial = initial;
|
||||
}
|
||||
|
||||
public void setSwitchChange(Combination combination, LightboxState newState)
|
||||
{
|
||||
switches[combination.ordinal()] = newState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* 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.
|
||||
*
|
||||
* 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.runelite.client.plugins.puzzlesolver.lightbox;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public class LightboxState
|
||||
{
|
||||
private final boolean[][] state = new boolean[LightBox.WIDTH][LightBox.HEIGHT];
|
||||
|
||||
public void setState(int x, int y, boolean s)
|
||||
{
|
||||
state[x][y] = s;
|
||||
}
|
||||
|
||||
public boolean getState(int x, int y)
|
||||
{
|
||||
return state[x][y];
|
||||
}
|
||||
|
||||
public LightboxState diff(LightboxState other)
|
||||
{
|
||||
LightboxState newState = new LightboxState();
|
||||
|
||||
for (int i = 0; i < LightBox.WIDTH; ++i)
|
||||
{
|
||||
for (int j = 0; j < LightBox.HEIGHT; ++j)
|
||||
{
|
||||
newState.state[i][j] = state[i][j] ^ other.state[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user