0% found this document useful (0 votes)
84 views18 pages

Mancala

The document describes the rules and implementation of an AI system for playing the mancala board game. It includes classes for representing game states, actions, and a problem state. Methods are provided for parsing input, executing actions, and determining the next state and best move using algorithms like minimax and alpha-beta pruning.

Uploaded by

Ajay Malalikar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
84 views18 pages

Mancala

The document describes the rules and implementation of an AI system for playing the mancala board game. It includes classes for representing game states, actions, and a problem state. Methods are provided for parsing input, executing actions, and determining the next state and best move using algorithms like minimax and alpha-beta pruning.

Uploaded by

Ajay Malalikar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

import

import
import
import
import
import
import
import

java.io.BufferedReader;
java.io.BufferedWriter;
java.io.File;
java.io.FileReader;
java.io.FileWriter;
java.io.IOException;
java.util.Arrays;
java.util.LinkedList;

/**
*
* @author Ajay
*/
public class mancala {
public static void main(String[] args) throws IOException {
File f = new File("next_state.txt");
if (!f.exists()) {
f.createNewFile();
}
try (BufferedWriter writer = new BufferedWriter(new
FileWriter(f))) {
//Clears out existing file contents
writer.write("");
if (args.length > 1) {
String inputFile = args[1] != null ? args[1] : "";
if(!inputFile.contains(".txt"))
{
inputFile = inputFile.concat(".txt");
}
parseInputAndProcess(inputFile, writer);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
static ProblemState prbState;
static void parseInputAndProcess(String fileName, BufferedWriter
writer) throws Exception {
try {
BufferedReader reader = new BufferedReader(new
FileReader(fileName));
int task = Integer.parseInt(reader.readLine());
int player = Integer.parseInt(reader.readLine());
int cutOffDepth = Integer.parseInt(reader.readLine());
String player2StateRaw = reader.readLine().trim();
String[] temp = player2StateRaw.split(" ");
int[] player2State = new int[temp.length];
for (int i = 0; i < temp.length; i++) {
player2State[i] = Integer.parseInt(temp[i]);
}

String player1StateRaw = reader.readLine().trim();


temp = player1StateRaw.split(" ");
int[] player1State = new int[temp.length];
for (int i = 0; i < temp.length; i++) {
player1State[i] = Integer.parseInt(temp[i]);
}
int numOfStonesInPlayer2Mancala =
Integer.parseInt(reader.readLine());
int numOfStonesInPlayer1Mancala =
Integer.parseInt(reader.readLine());
prbState = new ProblemState(task, player, cutOffDepth, null);
prbState.state = new MancalaState(player1State, player2State,
numOfStonesInPlayer1Mancala, numOfStonesInPlayer2Mancala, player);
long start = System.nanoTime();
playMancala(prbState, writer);
long end = System.nanoTime();
float totalTime = (end-start)/1000000;
totalTime = totalTime / 1000;
System.out.println(totalTime);
} catch (IOException | NumberFormatException e) {
throw e;
}
}
private static void playMancala(ProblemState prb, BufferedWriter
writer) throws Exception {
try {
MancalaState mancalaState = null;
switch (prb.taskType) {
case 1:
mancalaState = Greedy.processGreedy(prbState.state);
break;
case 2:
mancalaState =
MiniMax.processMinimax(prbState.state);
break;
case 3:
default:
mancalaState =
AlphaBeta.processAlphaBeta(prbState.state);
break;
case 4:
break;
}
if (mancalaState != null) {
writer.append(mancalaState.toString());
}
} catch (Exception e) {
throw e;
}
}
}

class ProblemState {
int taskType;
int player;
int cutOffDepth;
MancalaState state;
public ProblemState(int taskType, int player, int cutOffDepth,
MancalaState state) {
this.taskType = taskType;
this.player = player;
this.cutOffDepth = cutOffDepth;
this.state = state;
}
}
class Action {
int stones;
int pit;
public Action(int stones, int pit) {
this.stones = stones;
this.pit = pit;
}
}
class MancalaState {
String nodeName;
String nodeData;
int[] player1State;
int[] player2State;
int numOfStonesInPlayer1Mancala;
int numOfStonesInPlayer2Mancala;
int player;
int rootPlayer;
int numOfPits;
boolean hasToPlayAgain = false;
int depth;
boolean playerSwitched = false;
// Used to find next state in Minimax and Alpha Beta
MancalaState child = null;
int eval = Integer.MAX_VALUE;
boolean isGameOver = false;
boolean isLeafNode = false;
// Alpha Beta related properties
int alpha = Integer.MIN_VALUE;
int beta = Integer.MAX_VALUE;
public MancalaState(MancalaState state) {

this.player1State = state.player1State.clone();
this.player2State = state.player2State.clone();
this.numOfStonesInPlayer1Mancala =
state.numOfStonesInPlayer1Mancala;
this.numOfStonesInPlayer2Mancala =
state.numOfStonesInPlayer2Mancala;
this.player = state.player;
this.numOfPits = state.player1State.length;
this.nodeName = state.nodeName;
this.rootPlayer = state.rootPlayer;
this.eval = state.eval;
this.alpha = state.alpha;
this.beta = state.beta;
}
public MancalaState(MancalaState state, int alpha, int beta) {
this(state);
this.alpha = alpha;
this.beta = beta;
}
public MancalaState(int[] player1State, int[] player2State, int
numOfStonesInPlayer1Mancala, int numOfStonesInPlayer2Mancala, int player)
{
this.player1State = player1State.clone();
this.player2State = player2State.clone();
this.numOfStonesInPlayer1Mancala = numOfStonesInPlayer1Mancala;
this.numOfStonesInPlayer2Mancala = numOfStonesInPlayer2Mancala;
this.player = player;
this.rootPlayer = player;
this.numOfPits = player1State.length;
this.hasToPlayAgain = false;
firstInit();
}
public int evaluation() {
return this.rootPlayer == 1 ? this.numOfStonesInPlayer1Mancala this.numOfStonesInPlayer2Mancala
: this.numOfStonesInPlayer2Mancala this.numOfStonesInPlayer1Mancala;
}
public void calculateEval() {
this.eval = this.evaluation();
}
private void firstInit(){
this.depth = 0;
this.hasToPlayAgain = true;
this.nodeName = "root";
this.eval = Integer.MIN_VALUE;
}
private int numOfStonesInPlayer1Pit() {

int retVal = 0;
for (int i : this.player1State) {
retVal += i;
}
return retVal;
}
private int numOfStonesInPlayer2Pit() {
int retVal = 0;
for (int i : this.player2State) {
retVal += i;
}
return retVal;
}
public LinkedList<Action> GetActions() {
LinkedList<Action> actions = new LinkedList<>();
if (this.player == 1) {
for (int i = 0; i < player1State.length; i++) {
if (player1State[i] != 0) {
actions.add(new Action(player1State[i], i));
}
}
} else {
for (int i = 0; i < player2State.length; i++) {
if (player2State[i] != 0) {
actions.add(new Action(player2State[i], i));
}
}
}
return actions;
}
public MancalaState ExcuteAction(Action action, int depth) {
MancalaState state = new MancalaState(this);
state.depth = depth;
try {
int last = Integer.MAX_VALUE;
state.eval = state.depth % 2 == 0 ? Integer.MIN_VALUE :
Integer.MAX_VALUE;
if (state.player == 1) {
state.nodeName = "B" + (action.pit + 2);
// Updating all the pits and mancalas if stones complete
rotations
state.player1State[action.pit] = 0;
int numOfRevolutionRequired = action.stones /
((state.numOfPits * 2) + 1);
state.updateStonesInMancala(numOfRevolutionRequired);
action.stones -= numOfRevolutionRequired *
((state.numOfPits * 2) + 1);

// After revolution if no stones are left then last stone


was put in empt pit. Then empty that one and opposite pit
if(numOfRevolutionRequired == 1 && action.stones == 0){
int temp = state.player2State[action.pit] + 1;
state.numOfStonesInPlayer1Mancala += temp;
state.player1State[action.pit] = 0;
state.player2State[action.pit] = 0;
}
// Fill pits to the right
if (action.stones != 0) {
for (int i = action.pit + 1; i < state.numOfPits &&
action.stones != 0; i++) {
state.player1State[i] += 1;
--action.stones;
if (action.stones == 0) {
last = i;
}
}
}
// Fill Player's mancala
if (action.stones != 0) {
state.numOfStonesInPlayer1Mancala += 1;
--action.stones;
if (action.stones == 0) {
state.hasToPlayAgain = true;
}
}
// Fill Opposition's pits
if (action.stones != 0) {
for (int i = state.numOfPits - 1; i >= 0 &&
action.stones != 0; i--) {
state.player2State[i] += 1;
--action.stones;
}
}
// Fill pits to the left
if (action.stones != 0) {
for (int i = 0; i < action.pit && action.stones != 0;
i++) {
state.player1State[i] += 1;
--action.stones;
if (action.stones == 0) {
last = i;
}
}
}
// Check where the last stone was put
if (last <= state.numOfPits - 1 && last >= 0) {

if (state.player1State[last] == 1) {
int temp = state.player2State[last] + 1;
state.numOfStonesInPlayer1Mancala += temp;
state.player2State[last] = 0;
state.player1State[last] = 0;
}
}
} else {
state.nodeName = "A" + (action.pit + 2);
// Updating all the pits and mancalas if stones complete
rotations
state.player2State[action.pit] = 0;
int numOfRevolutionRequired = action.stones /
((state.numOfPits * 2) + 1);
state.updateStonesInMancala(numOfRevolutionRequired);
action.stones -= numOfRevolutionRequired *
((state.numOfPits * 2) + 1);
// After revolution if no stones are left then last stone
was put in empt pit. Then empty that one and opposite pit
if(numOfRevolutionRequired == 1 && action.stones == 0){
int temp = state.player1State[action.pit] + 1;
state.numOfStonesInPlayer2Mancala += temp;
state.player1State[action.pit] = 0;
state.player2State[action.pit] = 0;
}
// Fill pits to the left
if (action.stones != 0) {
for (int i = action.pit - 1; i >= 0 && action.stones
!= 0; i--) {
state.player2State[i] += 1;
--action.stones;
if (action.stones == 0) {
last = i;
}
}
}
// Fill Player's mancala
if (action.stones != 0) {
state.numOfStonesInPlayer2Mancala += 1;
--action.stones;
if (action.stones == 0) {
state.hasToPlayAgain = true;
}
}
// Fill Opposition's pits
if (action.stones != 0) {

for (int i = 0; i < state.numOfPits && action.stones


!= 0; i++) {
state.player1State[i] += 1;
--action.stones;
}
}
// Fill pits to the right
if (action.stones != 0) {
for (int i = state.numOfPits - 1; i >= 0 &&
action.stones != 0; i--) {
state.player2State[i] += 1;
--action.stones;
if (action.stones == 0) {
last = i;
}
}
}
// Check where the last stone was put
if (last <= state.numOfPits - 1 && last >= 0) {
if (state.player2State[last] == 1) {
int temp = state.player1State[last] + 1;
state.numOfStonesInPlayer2Mancala += temp;
state.player1State[last] = 0;
state.player2State[last] = 0;
}
}
}
// Special condition, where if my move makes all my pits zero
then all the stones in opponents pits will
// go to his mancala, thereby reducing the evaluation.
if (state.numOfStonesInPlayer1Pit() == 0) {
state.numOfStonesInPlayer2Mancala +=
state.numOfStonesInPlayer2Pit();
for (int i = 0; i < numOfPits; i++) {
state.player2State[i] = 0;
}
state.isGameOver = true;
}
else if (state.numOfStonesInPlayer2Pit() == 0) {
state.numOfStonesInPlayer1Mancala +=
state.numOfStonesInPlayer1Pit();
for (int i = 0; i < state.numOfPits; i++) {
state.player1State[i] = 0;
}
state.isGameOver = true;
}
if (state.depth == mancala.prbState.cutOffDepth &&
!state.hasToPlayAgain) {
state.isLeafNode = true;

if (state.eval == Integer.MAX_VALUE || state.eval ==


Integer.MIN_VALUE) {
state.eval = state.evaluation();
}
} else {
if (state.hasToPlayAgain) {
state.eval = state.eval == Integer.MAX_VALUE ?
Integer.MIN_VALUE : Integer.MAX_VALUE;
}
}
state.updateNodedata();
return state;
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
public void updateNodedata(int depth) {
StringBuilder sb = new StringBuilder();
sb.append(this.nodeName);
sb.append(",").append(depth);
sb.append(",").append(this.eval == Integer.MAX_VALUE ? "Infinity"
: (this.eval == Integer.MIN_VALUE ? "-Infinity" : this.eval));
if (mancala.prbState.taskType == 3) {
sb.append(",").append(this.alpha == Integer.MIN_VALUE ? "Infinity" : this.alpha);
sb.append(",").append(this.beta == Integer.MAX_VALUE ?
"Infinity" : this.beta);
}
sb.append(System.getProperty("line.separator"));
this.nodeData = sb.toString();
}
public void updateNodedata() {
this.updateNodedata(this.depth);
}
private void updateStonesInMancala(int number) {
for (int i = 0; i < player1State.length; i++) {
player1State[i] += number;
}
for (int i = 0; i < player2State.length; i++) {
player2State[i] += number;
}
if(this.player == 1)
numOfStonesInPlayer1Mancala += number;
else numOfStonesInPlayer2Mancala += number;
}
@Override
public String toString() {

return buildStringData(player2State, player1State,


numOfStonesInPlayer2Mancala, numOfStonesInPlayer1Mancala);
}
private String buildStringData(int[] player2State, int[]
player1State, int player2Mancala, int player1Mancala) {
StringBuilder sb = new StringBuilder();
String p2 = Arrays.toString(player2State).replace(',', '
').replace("[", "").replace("]", "");
String p1 = Arrays.toString(player1State).replace(',', '
').replace("[", "").replace("]", "");
sb.append(p2).append(System.getProperty("line.separator"));
sb.append(p1).append(System.getProperty("line.separator"));
sb.append(player2Mancala).append(System.getProperty("line.separator"));
sb.append(player1Mancala);
return sb.toString();
}
}
class Greedy {
public static MancalaState processGreedy(MancalaState currentState) {
return findGreedySolution(currentState);
}
private static MancalaState findGreedySolution(MancalaState state) {
LinkedList<MancalaState> list = new LinkedList<>();
LinkedList<MancalaState> leafNodes = new LinkedList<>();
MancalaState optimalState = null;
MancalaState temp;
list.add(state);
do {
MancalaState top = list.remove();
if (top.hasToPlayAgain) {
for (Action action : top.GetActions()) {
temp = top.ExcuteAction(action, 0);
list.add(temp);
if (!temp.hasToPlayAgain || temp.isGameOver) {
leafNodes.add(temp);
}
}
}
} while (!list.isEmpty());
for (MancalaState leafNode : leafNodes) {
if(optimalState == null)
optimalState = leafNode;
if (leafNode.evaluation() > optimalState.evaluation()) {
optimalState = leafNode;
}
}
return optimalState == null ? state : optimalState;
}

}
class MiniMax {
static BufferedWriter writer;
private static void init() throws IOException {
try {
File f = new File("traverse_log.txt");
if (!f.exists()) {
f.createNewFile();
}
writer = new BufferedWriter(new FileWriter(f));
writer.write("Node,Depth,Value");
writer.append(System.getProperty("line.separator"));
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static MancalaState processMinimax(MancalaState state) throws
IOException {
if (writer == null) {
init();
}
state.updateNodedata();
writer.append(state.nodeData);
MancalaState temp = MIN_VALUE(state);
MancalaState optimalState = null;
boolean found = false;
while(!found){
optimalState = temp.child;
if(optimalState != null && optimalState.child != null){
temp = optimalState;
optimalState = optimalState.child;
}
else found = true;
}
writer.close();
return optimalState == null ? state : optimalState;
}
private static MancalaState MAX_VALUE(MancalaState state) throws
IOException {
if (state.hasToPlayAgain) {
for (Action a : state.GetActions()) {
MancalaState temp = state.ExcuteAction(a, state.depth);
writer.append(temp.nodeData);
MancalaState t = MAX_VALUE(temp);

printEvaluatedValForGameOver(t, temp.nodeData);
state.eval = min(state.eval, t.eval);
if (state.playerSwitched) {
state.updateNodedata(state.depth - 1);
} else {
state.updateNodedata();
}
writer.append(state.nodeData);
}
return state;
}
if (state.depth == mancala.prbState.cutOffDepth) {
return state;
}
if (!state.hasToPlayAgain) {
MancalaState t = new MancalaState(state);
t.depth = state.depth + 1;
if (state.player == 1) {
t.player = 2;
} else {
t.player = 1;
}
t.playerSwitched = true;
t.hasToPlayAgain = true;
MancalaState temp = MIN_VALUE(t);
state.eval = max(state.eval, temp.eval);
return state;
}
return state;
}
private static MancalaState MIN_VALUE(MancalaState state) throws
IOException {
if (state.hasToPlayAgain) {
for (Action a : state.GetActions()) {
MancalaState temp = state.ExcuteAction(a, state.depth ==
0 ? 1 : state.depth);
writer.append(temp.nodeData);
MancalaState t = MIN_VALUE(temp);
printEvaluatedValForGameOver(t, temp.nodeData);
max(state, t);
if (state.playerSwitched) {
state.updateNodedata(state.depth - 1);
} else {
state.updateNodedata();
}

writer.append(state.nodeData);
}
return state;
}
if (state.depth == mancala.prbState.cutOffDepth) {
return state;
}
if (!state.hasToPlayAgain) {
MancalaState t = new MancalaState(state);
t.depth = state.depth + 1;
if (state.player == 1) {
t.player = 2;
} else {
t.player = 1;
}
t.playerSwitched = true;
t.hasToPlayAgain = true;
MancalaState temp = MAX_VALUE(t);
state.eval = min(state.eval, temp.eval);
return state;
}
return state;
}
private static int min(int s1, int s2) {
return s1 < s2 ? s1 : s2;
}
private static int max(int s1, int s2) {
if (s1 == Integer.MAX_VALUE) {
return s2;
} else if (s2 == Integer.MAX_VALUE) {
return s1;
} else {
return s1 > s2 ? s1 : s2;
}
}
private static void max(MancalaState s1, MancalaState s2) {
if (s1.eval == Integer.MAX_VALUE) {
s1.eval = s2.eval;
s1.child = s2;
} else {
if(s2.eval > s1.eval)
{
s1.eval = s2.eval;
s1.child = s2;
}
}

}
private static void printEvaluatedValForGameOver(MancalaState state,
String prevData) throws IOException
{
if(state.isGameOver){
state.calculateEval();
if (state.playerSwitched) {
state.updateNodedata(state.depth - 1);
} else {
state.updateNodedata();
}
if(!prevData.endsWith(state.nodeData)){
writer.append(state.nodeData);
}
}
}
}
class AlphaBeta {
static BufferedWriter writer;
private static void init() throws IOException {
try {
File f = new File("traverse_log.txt");
if (!f.exists()) {
f.createNewFile();
}
writer = new BufferedWriter(new FileWriter(f));
writer.write("Node,Depth,Value,Alpha,Beta");
writer.append(System.getProperty("line.separator"));
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static MancalaState processAlphaBeta(MancalaState state)
throws IOException
{
if (writer == null) {
init();
}
state.updateNodedata();
writer.append(state.nodeData);
MancalaState temp = Min_Value(state);
MancalaState optimalState = null;
boolean found = false;
while(!found){
optimalState = temp.child;
if(optimalState != null && optimalState.child != null){
temp = optimalState;
optimalState = optimalState.child;

}
else found = true;
}
writer.close();
return optimalState == null ? state : optimalState;
}
private static MancalaState Max_Value(MancalaState state) throws
IOException
{
if (state.hasToPlayAgain) {
for (Action a : state.GetActions()) {
MancalaState temp = state.ExcuteAction(a, state.depth);
writer.append(temp.nodeData);
MancalaState t = Max_Value(temp);
printEvaluatedValForGameOver(t, temp.nodeData);
// Player at max level having another chance. [Min node]
if(!state.playerSwitched && mancala.prbState.player !=
state.player && !state.isLeafNode){
state.eval = min(state.eval, t.eval);
if(state.alpha >= min(state.beta, t.eval)){
state.updateNodedata();
writer.append(state.nodeData);
state.beta = min(state.beta, t.eval);
}
else if(state.alpha < state.beta){
state.beta = min(state.beta, t.eval);
state.updateNodedata();
writer.append(state.nodeData);
}
}
// Player at min level [Max node]
else if(state.playerSwitched && !state.isLeafNode){
state.eval = min(state.eval, temp.eval);
if(state.alpha >= min(state.eval, t.eval)){
state.updateNodedata(state.depth-1);
writer.append(state.nodeData);
state.beta = min(state.beta, temp.eval);
}
else if(state.alpha < state.beta){
state.beta = min(state.beta, temp.eval);
state.updateNodedata(state.depth-1);
writer.append(state.nodeData);
}
}
if(state.alpha >= state.beta)
return state;
}
return state;

}
if (state.depth == mancala.prbState.cutOffDepth) {
return state;
}
if (!state.hasToPlayAgain) {
MancalaState t = new MancalaState(state);
t.depth = state.depth + 1;
if (state.player == 1) {
t.player = 2;
} else {
t.player = 1;
}
t.playerSwitched = true;
t.hasToPlayAgain = true;
MancalaState temp = Min_Value(t);
state.eval = temp.eval;
state.beta = temp.beta;
state.alpha = temp.alpha;
return state;
}
return state;
}
private static MancalaState Min_Value(MancalaState state) throws
IOException
{
if (state.hasToPlayAgain) {
for (Action a : state.GetActions()) {
MancalaState temp = state.ExcuteAction(a, state.depth ==
0 ? 1 : state.depth);
writer.append(temp.nodeData);
MancalaState t = Min_Value(temp);
printEvaluatedValForGameOver(t, temp.nodeData);
// Player at min level having another chance. [Max node]
if(!state.playerSwitched && mancala.prbState.player ==
state.player && !state.isLeafNode){
if(t.eval > state.eval){
state.eval = t.eval;
state.child = t;
state.updateNodedata();
}
if(state.beta <= max(state.alpha, t.eval)){
state.updateNodedata();
writer.append(state.nodeData);
max(state, t);

}
else if(state.alpha < state.beta){
max(state, t);
state.updateNodedata();
writer.append(state.nodeData);
}
}
// Player at max level [Min node]
else if(state.playerSwitched && !state.isLeafNode){
state.eval = max(state.eval, temp.eval);
if(state.beta <= max(state.alpha, t.eval)){
state.updateNodedata(state.depth-1);
writer.append(state.nodeData);
state.alpha = max(state.alpha, temp.eval);
}
else if(state.alpha < state.beta){
state.alpha = max(state.alpha, temp.eval);
state.updateNodedata(state.depth-1);
writer.append(state.nodeData);
}
}
if(state.beta <= state.alpha)
return state;
}
return state;
}
if (state.depth == mancala.prbState.cutOffDepth) {
return state;
}
if (!state.hasToPlayAgain) {
MancalaState t = new MancalaState(state);
t.depth = state.depth + 1;
if (state.player == 1) {
t.player = 2;
} else {
t.player = 1;
}
t.playerSwitched = true;
t.hasToPlayAgain = true;
MancalaState t1 = Max_Value(t);
state.eval = t1.eval;
state.beta = t1.beta;
state.alpha = t1.alpha;
return state;
}

return state;
}
private static int min(int s1, int s2) {
return s1 < s2 ? s1 : s2;
}
private static int max(int s1, int s2) {
if (s1 == Integer.MAX_VALUE) {
return s2;
} else if (s2 == Integer.MAX_VALUE) {
return s1;
} else {
return s1 > s2 ? s1 : s2;
}
}
private static void max(MancalaState s1, MancalaState s2) {
if(s2.eval > s1.alpha){
s1.alpha = s2.eval;
s1.child = s2;
}
}
private static void printEvaluatedValForGameOver(MancalaState state,
String prevData) throws IOException
{
if(state.isGameOver){
state.calculateEval();
if (state.playerSwitched) {
state.updateNodedata(state.depth - 1);
} else {
state.updateNodedata();
}
if(!prevData.equals(state.nodeData)){
writer.append(state.nodeData);
}
}
}
}

You might also like