package Matrix2P;

import Matrix2P.GameAlgorithm;
import Matrix2P.GameMatrix;

import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.*;

/**
 * Author: Max Buegler - max@sarstech.com
 * Date: Oct 12, 2009
 * Time: 7:09:51 PM
 */
public class LemkeHowsonAlgorithm implements GameAlgorithm {
    private int firstAction;
    private boolean stop=false;

    public LemkeHowsonAlgorithm(){
        this.firstAction=0;
        this.stop=false;
    }

    public void settings(ActionListener ul){
        final ActionListener ul2=ul;
        final JFrame settingsFrame=new JFrame("Lemke Howon Algorithm settings");
                settingsFrame.setLayout(new GridBagLayout());
                GridBagConstraints cons=new GridBagConstraints();
                cons.weightx=0.5;
                cons.weighty=0.5;
                cons.gridx=0;
                cons.gridy=0;
                cons.gridheight=1;
                cons.gridwidth=1;
                cons.fill=GridBagConstraints.HORIZONTAL;
                JPanel aboutPanel=new JPanel();
                aboutPanel.setBorder(BorderFactory.createCompoundBorder(
                                BorderFactory.createTitledBorder("About"),
                                BorderFactory.createEmptyBorder(5,5,5,5)));
                    aboutPanel.setLayout(new GridLayout(1,1));
                    JTextArea about=new JTextArea();
                    about.setEditable(false);
                    about.setCursor(null);
                    about.setOpaque(false);
                    about.setFocusable(false);
                    about.setLineWrap(true);
                    about.setText("Uses the Lemke-Howson algorithm to find equilibriums.");
                    aboutPanel.add(about);
                settingsFrame.add(aboutPanel,cons);

                JPanel settingsPanel=new JPanel();
                    settingsPanel.setLayout(new GridLayout(2,2));
                    settingsPanel.add(new JLabel("First action"));
                    final JTextField a1=new JTextField((firstAction+1)+"");
                    settingsPanel.add(a1);

                    JPanel button1=new JPanel();
                        button1.setLayout(new GridLayout(1,2));
                        JButton ok=new JButton("OK");
                        ok.addActionListener(new ActionListener(){
                            public void actionPerformed(ActionEvent e) {
                                try{
                                    firstAction=Integer.parseInt(a1.getText())-1;
                                    if (firstAction<0)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive integer value for 'First action'");
                                    return;
                                }
                                ul2.actionPerformed(null);
                                settingsFrame.setVisible(false);
                            }
                        });
                        button1.add(new JLabel());
                        button1.add(ok);
                        settingsPanel.add(button1);
                    JPanel button2=new JPanel();
                        button2.setLayout(new GridLayout(1,2));
                        JButton cancel=new JButton("Cancel");
                        cancel.addActionListener(new ActionListener(){
                            public void actionPerformed(ActionEvent e) {
                                settingsFrame.setVisible(false);
                            }
                        });
                        button2.add(cancel);
                        button2.add(new JLabel());
                        settingsPanel.add(button2);
                cons=new GridBagConstraints();
                cons.weightx=0.5;
                cons.weighty=0.5;
                cons.gridx=0;
                cons.gridy=1;
                cons.gridheight=3;
                cons.gridwidth=1;
                cons.fill=GridBagConstraints.HORIZONTAL;
                settingsFrame.add(settingsPanel,cons);



                settingsFrame.setSize(400,400);
                settingsFrame.setVisible(true);

    }

    public void run(GameMatrix m, ActionListener finish) {
        stop=false;
        LHSimplexMatrix lhm=new LHSimplexMatrix(m);
        if (firstAction<m.getActionsP1()){
            lhm.pivot(0,firstAction,0);
            int br=m.getP2BestReply(firstAction);
            System.out.println("Player 2 best reply to Action "+(firstAction+1)+" => "+(br+1));
            lhm.pivot(1,br,0);
            lhm.pivot(0,m.getActionsP1()+m.getActionsP2(),br+1);
            int br2=m.getP1BestReply(br);
            System.out.println("Player 1 best reply to Action "+(br+1)+" => "+(br2+1));
            lhm.pivot(1,m.getActionsP1()+m.getActionsP2(),br2+1);
            int l=lhm.getLabelOverlap();
            if (l>=0)System.out.println("Label overlap "+(l+1));
            else System.out.println("No Label overlap => Finished.");
            while(l!=-1&&!stop){
                int py=lhm.findPivot(0,l);
                lhm.pivot(0,l,py);
                l=lhm.getLabelOverlap();
                if (l>=0)System.out.println("Label overlap "+(l+1));
                else System.out.println("No Label overlap => Finished.");
                if (l!=-1){
                    if (l>=m.getActionsP1())l-=m.getActionsP1();
                    else l+=m.getActionsP2();
                    py=lhm.findPivot(1,l);
                    lhm.pivot(1,l,py);
                    l=lhm.getLabelOverlap();
                    if (l>=0)System.out.println("Label overlap "+(l+1));
                    else System.out.println("No Label overlap => Finished.");
                }
            }
            if (!stop){
                System.out.println("Found equilibrium: ");
                double[] p1=lhm.getPP1Fractions();
                double[] p2=lhm.getPP2Fractions();
                System.out.println("  Fractions:");
                System.out.print("    Player 1: (");
                for (int x=1;x<p1.length;x++){
                    if (p1[x]==0)System.out.print("0");
                    else if (p1[0]==1){
                        if ((int)p1[x]!=p1[x])
                            System.out.print(p1[x]);
                        else
                            System.out.print((int)p1[x]);
                    }
                    else if ((int)p1[x]!=p1[x]||(int)p1[0]!=p1[0])
                        System.out.print(p1[x]+"/"+p1[0]);
                    else
                        System.out.print((int)p1[x]+"/"+(int)p1[0]);
                    if (x<p1.length-1)System.out.print(" , ");
                }
                System.out.println(")");
                System.out.print("    Player 2: (");
                for (int x=1;x<p2.length;x++){
                    if (p2[x]==0)System.out.print("0");
                    else if (p2[0]==1){
                        if ((int)p2[x]!=p2[x])
                            System.out.print(p2[x]);
                        else
                            System.out.print((int)p2[x]);
                    }
                    else if ((int)p2[x]!=p2[x]||(int)p2[0]!=p2[0])
                        System.out.print(p2[x]+"/"+p2[0]);
                    else
                        System.out.print((int)p2[x]+"/"+(int)p2[0]);
                    if (x<p2.length-1)System.out.print(" , ");
                }
                System.out.println(")");
                System.out.println();
                p1=lhm.getPP1();
                p2=lhm.getPP2();
                System.out.println("  Real values:");
                System.out.print("    Player 1: (");
                for (int x=0;x<p1.length;x++){
                    if (p1[x]==(int)p1[x])
                        System.out.print((int)p1[x]);
                    else System.out.print(p1[x]);
                    if (x<p1.length-1)System.out.print(" , ");
                }
                System.out.println(")");
                System.out.print("    Player 2: (");
                for (int x=0;x<p2.length;x++){
                    if (p2[x]==(int)p2[x])
                        System.out.print((int)p2[x]);
                    else System.out.print(p2[x]);
                    if (x<p2.length-1)System.out.print(" , ");
                }
                System.out.println(")");

            }
        }
        finish.actionPerformed(null);



    }

    public void stop() {
        stop=true;
    }

    public String getDescriptor() {
        return "Lemke-Howson algorithm [a1="+(firstAction+1)+"]";
    }
    static class LHSimplexMatrix{
        private double d1,d2;
        private double[][] m1,m2;
        private int[] l1,l2;
        private boolean uw1,uw2;
        private GameMatrix m;
        public LHSimplexMatrix(GameMatrix m){
            this.d1=1;
            this.d2=1;
            this.m=m;
            this.uw1=false;
            this.uw2=false;
            m1=new double[m.getActionsP1()+m.getActionsP2()+2][m.getActionsP2()+1];
            l1=new int[m.getActionsP2()+1];
            l1[0]=-1;
            for (int x=0;x<m.getActionsP2();x++){
                 l1[x+1]=m.getActionsP1()+x;
            }
            for (int x=0;x<m.getActionsP1();x++){
                m1[x][0]=1;
            }
            for (int x=m.getActionsP1();x<m.getActionsP1()+m.getActionsP2()+1;x++){
                m1[x][0]=0;
            }
            m1[m.getActionsP1()+m.getActionsP2()+1][0]=1;
            for (int x=0;x<m.getActionsP1();x++){
                for (int y=0;y<m.getActionsP2();y++){
                    m1[x][y+1]=m.getP2()[x][y];
                }
            }
            for (int y=0;y<m.getActionsP2();y++){
                for (int x=0;x<m.getActionsP2();x++){
                    if (x==y)m1[m.getActionsP1()+x][y+1]=1;
                    else m1[m.getActionsP1()+x][y+1]=0;
                }
                m1[m.getActionsP1()+m.getActionsP2()][y+1]=1;
                m1[m.getActionsP1()+m.getActionsP2()+1][y+1]=0;
            }

            m2=new double[m.getActionsP2()+m.getActionsP1()+2][m.getActionsP1()+1];
            l2=new int[m.getActionsP1()+1];
            l2[0]=-3;
            for (int x=0;x<m.getActionsP1();x++){
                 l2[x+1]=x;
            }
            for (int x=0;x<m.getActionsP2();x++){
                m2[x][0]=1;
            }
            for (int x=m.getActionsP2();x<m.getActionsP2()+m.getActionsP1()+1;x++){
                m2[x][0]=0;
            }
            m2[m.getActionsP2()+m.getActionsP1()+1][0]=1;
            for (int x=0;x<m.getActionsP2();x++){
                for (int y=0;y<m.getActionsP1();y++){
                    m2[x][y+1]=m.getP1()[y][x];
                }
            }
            for (int y=0;y<m.getActionsP1();y++){
                for (int x=0;x<m.getActionsP1();x++){
                    if (x==y)m2[m.getActionsP2()+x][y+1]=1;
                    else m2[m.getActionsP2()+x][y+1]=0;
                }
                m2[m.getActionsP2()+m.getActionsP1()][y+1]=1;
                m2[m.getActionsP2()+m.getActionsP1()+1][y+1]=0;
            }
            print();
        }

        public double[][] getM1() {
            return m1;
        }

        public double[][] getM2() {
            return m2;
        }

        public int getLabelOverlap(){
            boolean[] la1=new boolean[m.getActionsP1()+m.getActionsP2()];
            boolean[] la2=new boolean[m.getActionsP1()+m.getActionsP2()];
            for (int x=0;x<l1.length;x++){
                if (l1[x]>=0)la1[l1[x]]=true;
            }
            System.out.print("L1: ");
            for (int x=0;x<la1.length;x++){
                if (la1[x])System.out.print("_");
                else System.out.print((x+1));
            }
            System.out.println();
            for (int x=0;x<l2.length;x++){
                if (l2[x]>=0)la2[l2[x]]=true;
            }
            System.out.print("L2: ");
            for (int x=0;x<la2.length;x++){
                if (la2[x])System.out.print("_");
                else System.out.print((x+1));
            }
            System.out.println();

            for (int x=0;x<la1.length;x++){
                if (!la1[x]&&!la2[x])return x;
            }
            return -1;
        }

        public int[] getL1() {
            return l1;
        }

        public int[] getL2() {
            return l2;
        }

        public void print(){
            System.out.println("Player 1 Simplex:");
            for (int y=0;y<m1[0].length;y++){
                if (l1[y]>=0)
                    System.out.print((l1[y]+1)+"   |  ");
                else if (l1[y]==-1)
                    System.out.print("top |  ");
                else if (l1[y]==-2)
                    System.out.print("-w  |  ");
                for (int x=0;x<m1.length;x++){
                    if ((int)m1[x][y]!=m1[x][y]){
                        System.out.print(m1[x][y]);
                        for (int s=0;s<10-(m1[x][y]+"").length();s++)System.out.print(" ");
                    }
                    else{
                        System.out.print((int)m1[x][y]);
                        for (int s=0;s<10-((int)m1[x][y]+"").length();s++)System.out.print(" ");
                    }
                }
                if (y==m1[0].length-1){
                    if ((int)d1!=d1)System.out.println("  |d= "+d1);
                    else System.out.println("  |d= "+(int)d1);
                }
                else System.out.println();
            }
            System.out.println("Player 2 Simplex:");
            for (int y=0;y<m2[0].length;y++){
                if (l2[y]>=0)
                    System.out.print((l2[y]+1)+"   |  ");
                else if (l2[y]==-3)
                    System.out.print("top |  ");
                else if (l2[y]==-4)
                    System.out.print("-u  |  ");
                for (int x=0;x<m2.length;x++){
                    if ((int)m2[x][y]!=m2[x][y]){
                        System.out.print(m2[x][y]);
                        for (int s=0;s<10-(m2[x][y]+"").length();s++)System.out.print(" ");
                    }
                    else{
                        System.out.print((int)m2[x][y]);
                        for (int s=0;s<10-((int)m2[x][y]+"").length();s++)System.out.print(" ");
                    }
                }
                if (y==m2[0].length-1){
                    if ((int)d2!=d2)System.out.println("  |d= "+d2);
                    else System.out.println("  |d= "+(int)d2);
                }
                else System.out.println();
            }
        }

        public int findPivot(int pl,int x){
            if (pl==0){
                int max=-1;
                double maxratio=-Double.MAX_VALUE;
                for (int y=0;y<m1[x].length;y++){
                    if (m1[m1.length-1][y]!=0&&m1[x][y]>0){
                        double ratio=m1[x][y]/m1[m1.length-1][y];
                        if (ratio>maxratio){
                            maxratio=ratio;
                            max=y;
                        }
                    }

                }
                return max;
            }
            int max=-1;
            double maxratio=-Double.MAX_VALUE;
            for (int y=0;y<m2[x].length;y++){
                if (m2[m2.length-1][y]!=0&&m2[x][y]>0){
                    double ratio=m2[x][y]/m2[m2.length-1][y];
                    if (ratio>maxratio){
                        maxratio=ratio;
                        max=y;
                    }
                }
            }
            return max;
        }
        public double[] getPP1Fractions(){
            double[] out=new double[m.getActionsP1()+1];
            out[0]=d1;
            for (int y=1;y<out.length;y++)out[y]=0;
            for (int y=0;y<l1.length;y++){
                if (l1[y]>=0&&l1[y]<m.getActionsP1()){
                    out[l1[y]+1]=m1[m1.length-1][y];
                }
            }
            return out;
        }
        public double[] getPP2Fractions(){
            double[] out=new double[m.getActionsP2()+1];
            out[0]=d2;
            for (int y=1;y<out.length;y++)out[y]=0;
            for (int y=0;y<l2.length;y++){
                if (l2[y]>=m.getActionsP1()&&l2[y]<m.getActionsP1()+m.getActionsP2()){
                    out[l2[y]-m.getActionsP1()+1]=m2[m2.length-1][y];
                }
            }
            return out;
        }
        public double[] getPP1(){
            double[] out=new double[m.getActionsP1()];
            double sum=0;
            for (int y=0;y<out.length;y++)out[y]=0;
            for (int y=0;y<l1.length;y++){
                if (l1[y]>=0&&l1[y]<m.getActionsP1()){
                    out[l1[y]]=m1[m1.length-1][y]/m1[l1[y]][y];
                    sum+=m1[m1.length-1][y]/m1[l1[y]][y];
                }
            }
            for (int y=0;y<out.length;y++)out[y]/=sum;
            return out;
        }
        public double[] getPP2(){
            double[] out=new double[m.getActionsP2()];
            double sum=0;
            for (int y=0;y<out.length;y++)out[y]=0;
            for (int y=0;y<l2.length;y++){
                if (l2[y]>=m.getActionsP1()&&l2[y]<m.getActionsP1()+m.getActionsP2()){
                    out[l2[y]-m.getActionsP1()]=m2[m2.length-1][y]/m2[l2[y]-m.getActionsP1()][y];
                    sum+=m2[m2.length-1][y]/m2[l2[y]-m.getActionsP1()][y];
                }
            }
            for (int y=0;y<out.length;y++)out[y]/=sum;
            return out;
        }
        public boolean pivot(int pl,int px, int py){
            System.out.println("Pivoting Player "+(pl+1)+" Pivot: "+(px+1)+" , "+(py+1));
            if (pl==0){
                if (m1[px][py]==0)return false;
                //double d=m1[px][py];
                for (int y=0;y<m1[px].length;y++){
                    if (y!=py&&m1[px][y]!=0){
                        //double e=m1[px][y];
                        double d=m1[px][y];
                        for (int x=0;x<m1.length;x++){
                            m1[x][y]=(m1[px][py]*m1[x][y]-d*m1[x][py])/d1;
                        }
                    }
                }
                d1=m1[px][py];
                if (uw1==false){
                    if (px<m1.length-2)l1[py]=px;
                    else l1[py]=-2;
                }
                else{
                    if (px<m1.length-1)l1[py]=px;
                    else l1[py]=-2;
                }
                if (uw1==false&&px==m1.length-2){
                    uw1=true;
                    double[][] nm1=new double[m1.length-1][m1[0].length-1];
                    int[] nl1=new int[m1[0].length-1];
                    for (int y=0;y<m1[0].length-1;y++){
                        int oy=y;
                        if (y>=py)oy=y+1;
                        for (int x=0;x<m1.length-1;x++){
                            int ox=x;
                            if (x==m1.length-2)ox=m1.length-1;
                            nm1[x][y]=m1[ox][oy];
                        }
                        nl1[y]=l1[oy];
                    }
                    l1=nl1;
                    m1=nm1;
                }

            }
            else{
                if (m2[px][py]==0)return false;
                for (int y=0;y<m2[px].length;y++){
                    if (y!=py&&m2[px][y]!=0){
                        double d=m2[px][y];
                        for (int x=0;x<m2.length;x++){
                            m2[x][y]=(m2[px][py]*m2[x][y]-d*m2[x][py])/d2;
                        }
                    }
                }
                d2=m2[px][py];
                if (uw2==false){
                    if (px<m.getActionsP2())l2[py]=m.getActionsP1()+px;
                    else if (px<m2.length-2)l2[py]=px-m.getActionsP2();
                    else l2[py]=-4;
                }
                else{
                    if (px<m.getActionsP2())l2[py]=m.getActionsP1()+px;
                    else if (px<m2.length-1)l2[py]=px-m.getActionsP2();
                    else l2[py]=-4;

                }
                if (uw2==false&&px==m2.length-2){
                    uw2=true;
                    double[][] nm2=new double[m2.length-1][m2[0].length-1];
                    int[] nl2=new int[m2[0].length-1];
                    for (int y=0;y<m2[0].length-1;y++){
                        int oy=y;
                        if (y>=py)oy=y+1;
                        for (int x=0;x<m2.length-1;x++){
                            int ox=x;
                            if (x==m2.length-2)ox=m2.length-1;
                            nm2[x][y]=m2[ox][oy];
                        }
                        nl2[y]=l2[oy];
                    }
                    l2=nl2;
                    m2=nm2;
                }

            }
            print();
            return true;
        }
    }
    public static void main(String[] args){
        double[][] p1=new double[][]{{0,1,1,2},{2,-3,-2,0},{1,3,-1,-2}};
        double[][] p2=new double[][]{{0,-1,-1,-2},{-2,3,2,0},{-1,-3,1,2}};
        LHSimplexMatrix m=new LHSimplexMatrix(new GameMatrix(p1,p2));
        m.print();
        m.pivot(0,0,0);
        m.print();
        m.pivot(1,0,0);
        m.print();
    }
}
