/*
* Here comes the text of your license
* Each line should be prefixed with *
*/
package nars.lab.tictactoe;
/*
* Copyright (C) 2014 tc
*
* 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 <http://www.gnu.org/licenses/>.
*/
import automenta.vivisect.Video;
import automenta.vivisect.swing.NWindow;
import java.awt.BorderLayout;
import static java.awt.BorderLayout.CENTER;
import static java.awt.BorderLayout.SOUTH;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import nars.util.EventEmitter.EventObserver;
import nars.util.Events.FrameEnd;
import nars.storage.Memory;
import nars.NAR;
import nars.config.Parameters;
import nars.config.Plugins;
import nars.entity.BudgetValue;
import nars.entity.Concept;
import nars.entity.Task;
import nars.gui.NARSwing;
import nars.io.Output.OUT;
import nars.language.Term;
import nars.operator.Operation;
import nars.operator.Operator;
/**
*
* @author tc
*/
public class TicTacToe extends JPanel {
final boolean HUMAN = false;
final boolean COMPUTER = true;
final boolean STARTING_PLAYER = HUMAN;
boolean playing = STARTING_PLAYER;
/**
* Creates new form play
*/
public final NAR nar;
int[] field = new int[] {
0, 0, 0,
0, 0, 0,
0, 0, 0
};
private final JLabel status;
Set<Term> fieldTerms = new HashSet();
public TicTacToe() {
super(new BorderLayout());
nar = new NAR();
nar.memory.addOperator(new AddO("^addO"));
(nar.param).duration.set(1000);
(nar.param).noiseLevel.set(0);
new NARSwing(nar);
nar.on(FrameEnd.class, new EventObserver() {
@Override
public void event(Class event, Object[] args) {
// nar.memory.addSimulationTime(500);
}
});
nar.start(30);
JPanel menu = new JPanel(new FlowLayout());
JButton resetButton = new JButton("RESET");
resetButton.setBackground(Color.DARK_GRAY);
resetButton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
reset();
}
});
menu.add(resetButton);
status = new JLabel("");
menu.add(status);
JButton teachButton = new JButton("Look and Hurry!");
teachButton.setBackground(Color.DARK_GRAY);
teachButton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
teach();
}
});
menu.add(teachButton);
add(menu, SOUTH);
add(new GridButtonPanel(nar, 3, 3) {
@Override
public Concept initTerm(int x, int y) {
Term t = new Term( Integer.toString(y * 3 + x) );
fieldTerms.add(t);
return nar.memory.conceptualize(new BudgetValue(0.5f, 0.5f, 0.5f), t);
}
@Override
public void onMouseClick(GridButtonPanel.ConceptButton c, boolean press, int wheelRotation) {
int x = c.bx;
int y = c.by;
int i = index(c);
if ((playing != HUMAN) || field[i] != 0) {
return;
}
nar.addInput("<(*," + i + ",human) --> move>. :|:");
c.setText("X");
field[i] = 1;
playing = COMPUTER;
updateField();
}
@Override
public void onMouse(GridButtonPanel.ConceptButton c, boolean press, boolean hover, int wheel) {
//enable NARS to cheat a bit by watching where mouse cursor is
/*int i = index(c);
nar.addInput("$0.10$ <" + i + " --> moused>. :|:");*/
}
//TODO
/*
@Override
public void repaintOverlay(Graphics g) {
Set<Term> h = new HashSet();
for (Concept c : nar.memory.concepts) {
h.clear();
if (!(c.term instanceof CompoundTerm)) continue;
for (Term t : ((CompoundTerm)(c.term)).term)
h.add(t);
h.retainAll(fieldTerms);
if (h.size() >= 2)
System.out.print(h + " ");
}
System.out.println();
}
*/
public Color getBackgroundColor(float priority) {
return Color.getHSBColor(0.2f + priority * 0.2f, 0.75f, 0.5f + priority * 0.5f);
}
Color blue = new Color(0.5f, 0.5f, 1f);
Color red = new Color(1f, 0.5f, 0.5f);
@Override
public void repaintButton(GridButtonPanel.ConceptButton b) {
b.setBackground(getBackgroundColor(b.concept.getPriority()));
b.setFont(buttonFont);
String s = "";
switch (field[index(b)]) {
case 1: s = "O"; b.setForeground(blue); break;
case 2: s = "X"; b.setForeground(red); break;
}
b.setText(s);
}
}, CENTER);
reset();
}
public int index(GridButtonPanel.ConceptButton cp) {
return cp.bx + 3 * cp.by;
}
public class AddO extends Operator {
public AddO(String name) {
super(name);
}
@Override
protected List<Task> execute(Operation operation, Term[] args, Memory memory) {
int i = -1;
try {
i = Integer.parseInt( args[0].toString() );
}
catch (Throwable e) {
return null;
}
if (!((i >=0) && (i <=8)))
return null;
if (playing == COMPUTER) {
boolean success = false;
if (field[i]==0) {
field[i] = 2;
success = true;
status.setText("Humans Turn");
}
if (success) {
playing = HUMAN;
nar.emit(TicTacToe.class, "NARS plays: " + i);
nar.addInput("<input --> succeeded>. :|: %" + (success ? "1.00" : "0.00") + "%" );
nar.addInput("<(*," + i + ",nars) --> move>. :|:");
System.out.println( operation.getTask().getExplanation() );
updateField();
}
else {
nar.emit(TicTacToe.class, "NARS selects invalid cell: " + i);
nar.addInput("(--,<input --> succeeded>). :|:");
}
}
else {
nar.emit(TicTacToe.class, "NARS not playing, but wants: " + i);
nar.addInput("(--,<input --> succeeded>). :|:");
}
return null;
}
}
public int[][] howToWin = {
{ 0, 1, 2 },
{ 3, 4, 5 },
{ 6, 7, 8 },
{ 0, 3, 6 },
{ 1, 4, 7 },
{ 2, 5, 8 },
{ 0, 4, 8 },
{ 2, 4, 6 }
};
public void updateField() {
if (playing == COMPUTER)
status.setText("NARS Turn");
else
status.setText("Humans Turn");
Boolean winner = null;
for (int p = 1; p <= 2; p++) { // 1=human, 2=nars
for (int[] h : howToWin) {
if (field[h[0]]!=p) continue;
if (field[h[1]]!=p) continue;
if (field[h[2]]!=p) continue;
winner = p == 1 ? HUMAN : COMPUTER;
}
}
if (winner == null) {
String s = "";
for (int i = 0; i < 9; i++) {
String f = "empty";
switch (field[i]) {
case 1: f = "human"; break;
case 2: f = "nars"; break;
}
s += "<" + i + " --> " + f + ">. :|:\n";
}
nar.addInput(s);
}
else if (winner == HUMAN) {
status.setText("Human wins");
nar.emit(OUT.class, "Human wins");
nar.addInput("<human --> win>. :|: %1.0;0.99%");
nar.addInput("<nars --> win>. :|: %0.0;0.99%");
}
else if (winner == COMPUTER) {
status.setText("NARS wins");
nar.emit(OUT.class, "NARS wins");
nar.addInput("<human --> win>. :|: %0.0;0.99%");
nar.addInput("<nars --> win>. :|: %1.0;0.99%");
}
}
public void reset() {
playing = STARTING_PLAYER;
Arrays.fill(field, 0);
nar.addInput("<game --> reset>. :|:");
teach();
updateField();
}
public void teach() {
String rules = "";
rules+=("<nars --> win>! %1.0;0.99%\n");
//+"<(^addO,$1) =/> <input --> succeeded>>.\n"); //usually input succeeds
//+"<(&/,<1 --> set>,(^addO,$1)) =/> (--,<input --> succeeded>)>.\n"); //usually input succeeds but not when it was set by player cause overwrite is not valid
//+"<(&/,(^addO,$1),(^addO,$1)) =/> (--,<input --> succeeded>)>.\n"); //also overwriting on own is not valid
for (int[] h : howToWin) {
int a = h[0];
int b = h[1];
int c = h[2];
rules+=("<(&|,(^addO," + a + "),<input --> succeeded>,(^addO," + b + "),<input --> succeeded>,(^addO," + c + "),<input --> succeeded>) =/> <nars --> win>>.\n");
rules+=("<(&|,<" + a + " --> $1>,<" + b + " --> $1>,<" + c + " --> $1>) =/> <$1 --> win>>.\n");
}
//for NAL9 (anticipate)
if (nar.memory.getOperator("^anticipate")!=null) {
rules+=("<(&/,(--,<$1 --> empty>),(^add0,$1)) =/> (--,<input --> succeeded>)>>.\n");
rules+=("<(&/,(--,<$1 --> field>),(^add0,$1)) =/> (--,<input --> succeeded>)>.\n");
}
rules+=("<nars --> win>! %1.0;0.99%\n");
rules+=("<human --> win>! %0.0;0.99%\n");
rules+=("(&/,<#1 --> field>,(^addO,#1))!\n"); //doing something is also a goal :D
rules+=("(^addO,0)! %1.0;0.7%\n");
rules+=("(^addO,1)! %1.0;0.7%\n");
rules+=("(^addO,2)! %1.0;0.7%\n");
rules+=("(^addO,3)! %1.0;0.7%\n");
rules+=("(^addO,4)! %1.0;0.7%\n");
rules+=("(^addO,5)! %1.0;0.7%\n");
rules+=("(^addO,6)! %1.0;0.7%\n");
rules+=("(^addO,7)! %1.0;0.7%\n");
rules+=("(^addO,8)! %1.0;0.7%\n");
rules+=("<{nars,human,empty} <-> field>.\n");
rules+=("(||,<human --> win>,<nars --> win>).\n");
rules+=("<0 --> field>.\n");
rules+=("<1 --> field>.\n");
rules+=("<2 --> field>.\n");
rules+=("<3 --> field>.\n");
rules+=("<4 --> field>.\n");
rules+=("<5 --> field>.\n");
rules+=("<6 --> field>.\n");
rules+=("<7 --> field>.\n");
rules+=("<8 --> field>.\n");
rules+=("<input --> succeeded>!\n");
nar.addInput(rules);
updateField();
}
private static final Font buttonFont = Video.monofont.deriveFont(Font.BOLD).deriveFont(34f);
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
NARSwing.themeInvert();
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NWindow("NARTacToe", new TicTacToe()).show(400,400,true);
}
});
}
}