ag.algebraic geometry – Kernel sheaf of evaluation map

I have some questions on the kernel sheaf of the evaluation map.

Let $F$ be a globally generated rank $2$ torsion free stable sheaf on a smooth projective variety $X$. Then the evaluation map $ev: H^0(X, F)otimes mathcal{O}_X to F$ is surjective. Denote $K:=Ker(ev)$, Then I wonder that:

(1) Is $K$ always locally free?

(2) What can we talk about the stability of $K$?

Thanks for any help.

dnd 5e – Evaluation of homebrew powerattack feat for one-handed attacks

All questions of flavor aside, how balanced do you think this feat is compared to Great Weapon Master and Sharpshooter?

Adept Weapon User (AWU)

  • Before you make a melee attack using the Attack action with either a weapon that does not have the heavy property that you are proficient with, an unarmed strike, or an improvised weapon that you are proficient with, you can choose to take a -5 penalty to the attack roll. If the attack hits, you add +10 to the attack’s damage.
  • When you score a critical hit with a melee attack or reduce a creature to 0 hit points with one, you have advantage on the next attack you make until the end of your next turn.

The idea behind the design is simply to give all martial classes access to a powerattack feat and to bring dual wielding, one-handed weapons and monks DPR-wise in line with two-handed and ranged weapons.

I deliberately chose to make the power attack only applicable to the Attack action, because otherwise Two-Weapon Fighting and Flurry of Blows would become too strong. Furthermore, I wanted to avoid making two-handed weapons strictly worse than sword and board (or worse, polearm master spear and shield), and so this powerattack cannot be used with opportunity attacks, Haste attacks, the Ready action, bonus action attacks, or attacks from magic items (e.g. Scimitar of Speed, Dancing Sword). Additionally, the secondary feature of the feat is worse than that of Great Weapon Master. I also avoided clogging the bonus action, because monks and dual wielders already use this.

I have made some rough DPR calculations, but this is anything but rigorous. The biggest hurdle in a comparison is probably the fact that a player can choose to not use powerattack if the target’s AC is too high (or its HP too low). One would need to have a distribution of AC targets and weight the values for each AC accordingly.
To make this simpler, I have assumed a baseline hit chance of 65%, which is reduced to 40% when power attacking.
Comparing one hit of a two-handed fighter (+4 STR, Defense Fighting Style), a GWM fighter (+3 STR, Defense Fighting Style) to a baseline sword-and-board fighter (+4 STR and Dueling) and one with the new feat (+3 STR and Dueling):

2H-base: (2d6 + 4) * 0.65 = 7.150
2H-GWM: (2d6 + 13) * 0.35 = 7.000
1H-base: (1d8 + 6) * 0.65 = 6.825
1h-AWU: (1d8 + 15) * 0.35 = 6.825 (this is a coincidence)

Note that these numbers do not reflect the powerattacker’s option to not powerattack.

When the characters are high enough to get a +1 weapon and max out their STR:

2H-base: (2d6 + 6) * 0.70 = 9.100
2H-GWM: (2d6 + 16) * 0.45 = 10.350
1H-base: (1d8 + 8) * 0.70 = 8.750
1h-AWU: (1d8 + 18) * 0.45 = 10.125

Essentially, in this comparison a AWU-Fighter does slightly less damage than a GWM-Fighter per hit and has no additional chance for a bonus attack, but has +1 AC.
Dual wielding is harder to calculate, because the number of mainhand attacks also plays a role.

Any thoughts?

The only loophole I can find is the Path of the Beast Barbarian’s Claw attack, but even that is not excessively strong, in my opinion. Another downside of the current design is that the secondary effect is fairly weak for dual-wielding or sword and board barbarians.

Technically speaking it is possible to use both AWU and SS together when using a non-heavy ranged weapon as improvised weapon, similar to GWM and SS with a heavy ranged weapon (at least for a certain interpretation of the rules). I do not see this as a problem.

simplifying expressions – Simplification and numerical evaluation differ substantially

My question is very simple. I am trying to simplify an expression that I was expecting to be vanishing. However, If I numerically evaluate the expression (for large values of one parameter) I find 1, whereas if I simplify it I get 0, see the picture

enter image description here

Here’s the code to reproduce it

(m Sqrt(P + Sqrt(m^2 + P^2)) - Sqrt(-P + Sqrt(m^2 + P^2)) (P + Sqrt(m^2 + P^2))) 1/Sqrt(P + Sqrt(m^2 + P^2)) /. m -> 1 /. P -> 10^8 // N
(m Sqrt(P + Sqrt(m^2 + P^2)) - Sqrt(-P + Sqrt(m^2 + P^2)) (P + Sqrt(m^2 + P^2))) 1/Sqrt(P + Sqrt(m^2 + P^2)) // Simplify(#, {m > 0, P > 0}) &

What’s happening?

Equivalently, I have rescaled $mrightarrow x P$ and try to take the $xrightarrow 0^+$ limit. I get the error "Unable to determine whether expressions .... are equal to zero. Assuming they are."
enter image description here

Here’s the code

(m Sqrt(P + Sqrt(m^2 + P^2)) - Sqrt(-P + Sqrt(m^2 + P^2)) (P + Sqrt(m^2 + P^2))) 1/Sqrt(P + Sqrt(m^2 + P^2)) /. m -> x P // Simplify(#, P > 0) &
Limit(x - Sqrt((-1 + Sqrt(1 + x^2))/(1 + Sqrt(1 + x^2))) (1 + Sqrt(1 + x^2)), x -> 0,Direction -> "FromAbove")

This is strange because if I FullySimplify the expression under the limit, I get 0. What am I doing wrong?

How to assign very large outputs to function for evaluation at points?

I create a very large output

    D(x^100*E^(2*x^5)*Cos(x^2), {x, 137})

I want to assign this to a function as

    f(x_):= {very large output from previous command}

This allows me to evaluate that output for various values of $x$.

I tried

    Function(x, Evaluate(%11))

But it seems cumbersome and I wasn’t sure I was getting correct results from it.

Is there a clean way to do this?

evaluation – What’s the difference between Evaluate and Replace command?

all, I’m new to Mathematica, I am very confused between Evaluate and Replace (/.) command. I feel they are very similar.

For example,

In(2):= y = x + 2;

y /. {x -> 3}

Out(3)= 5

However, if I use
In(1):= y = x + 2;

Evaluate(y, x = 3)

Out(2)= Sequence(5, 3)

For both cases, I can get y=5.
I think for the convenience, we can always use Replace instead of Evaluate for the simple substitution, correct? Or is there any case I should use Evaluate only instead of using replace If I want to substitute a symbol to another symbol or value?

Thank you

evaluation – Automatically set x=0, after each input/output ? ($AfterEachInputOutput)

Is it possible to do something that I want, after each input/output automatically and silently?
I am looking for a feature like $AfterEachInputOutput in the following code :

Example : to set variable x to 0 after each output

In(1) $AfterEachInputOutput=(x=0;)
---> x=0 automatically and silently (not important because this is a starting step)
In(2) x=2;x+2
Out(2) 4
---> x=0 automatically and silently
In(3) If(x=0,100,-100)
Out(3) 100
---> x=0 automatically and silently
In(4) x=1;y=1;
---> x=0 automatically and silently
In(5) x+10
Out(5) 10
---> x=0 automatically and silently

I have tried

$Post=(x=0;)

or

$Preprint=(x=0;)

But it didn’t work well.

c++ – minmax evaluation with alpha beta pruning

I’m making some small board game and wanted to code a very simple greed AI with this algorithm. It turns out it doesn’t play the most greedy moves, it is simply not working. I’d appreciate any comments around this code.

First, the position evaluation functions are:

uint8_t get_piece_value(PKind kind)
{
    switch (kind) {
    case PKind::FIRST:
        return 10;
    case PKind::SECOND:
        return 30;
    case PKind::THIRD:
        return 35;
    case PKind::FORTH:
        return 100;
}

int get_position_value(const Position& position)
{
    int value;

    for (auto var : position.pieces) {

        if (var.id.color == position.turnToPlay) {
            value += get_piece_value(var.id.kind);
            continue;
        }
        
        value -= get_piece_value(var.id.kind);

    }

    return value;
}

Now this is the function I use to get the valid moves:

std::vector<PMove> get_ALL_valid_moves(const Position& position)
{
    std::vector<PMove> allMoves;

    for (auto piece : position.pieces)
    {
        if (piece.id.color == position.turnToPlay) {
            auto validMoves = get_valid_moves(piece);

            if (validMoves.size() == 0) {
                continue;
            }
            for (auto var : validMoves) {
                allMoves.push_back(var);
            }
        } else {
            assert(("Wrong color passed to get ALL valid moves!!n"));
        }
    }

    return allMoves;
}

Next, here are the minmax functions:

constexpr int MAX_EVAL = 9999;
constexpr int MIN_EVAL = -MAX_EVAL;

///Minmax evaluation with alpha beta pruning
int minmax_ab(const Position newposition, int depth, int alpha, int beta, bool isMaximizer) 
{
    if (depth == 0) {
        return get_position_value(newposition);
    }

    std::vector<PMove> validMoves;

    validMoves = get_ALL_valid_moves(newposition);

    if (validMoves.size() == 0) {
        return get_position_value(newposition);
    }

    if (isMaximizer) {
        for (auto move : validMoves) {
            alpha  = std::max(alpha, minmax_ab(make_position(newposition, move), depth - 1, alpha, beta, false) );
            if (alpha >= beta) {
                return beta;
            }
        }
        return alpha;

    } else {
        for (auto move : validMoves) {
            beta = std::min(beta, minmax_ab(make_position(newposition, move), depth - 1, alpha, beta, true) );
            if (beta <= alpha) {
                return alpha;
            }
        }
        return beta;
    }

}

PMove minmax_root(const Position& position, const int depth)
{

    std::vector<PMove> validMoves = get_ALL_valid_moves(position);
    /// assume the starting value of the last valid move for best move
    PMove bestmove = validMoves.front();

    int besteval = get_position_value(make_position(position, bestmove));

    int eval = MIN_EVAL;

    for (auto move : validMoves) {
        eval = minmax_ab(make_position(position, move), depth - 1, MIN_EVAL, MAX_EVAL, false);

        if (eval > besteval) {
            besteval = eval;
            bestmove = move;
        }
    }

    return bestmove;
}
```

java – Pokemon Soul Link tracker code evaluation

I’ve just finished writing a program to help track Pokemon across Soul Links. For those curious I will include a link to what a Soul Link is. The goal of the program is to take in the names of the two Pokemon alongside the route that they were caught on. The Pokemon is then added to a table for the user to keep track of whether or not the Pokemon has died. Any feedback is appreciated!

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;

public class PokeTracker extends JFrame {

    private static final long serialVersionUID = 1L;

    private static final int WIDTH = 600;
    private static final int HEIGHT = WIDTH / 2 + WIDTH / 6;
    private static final String TITLE = "PokéTracker";

    private String name1 = "Ash";
    private String name2 = "Brock";
    private int fontSize = 12;
    private int columns = 14;
    private int margin = fontSize / 2 + columns / 2;

    private List<Pokemon> pokemon;
    private DefaultTableModel model;

    public PokeTracker() throws IOException {
        this.pokemon = new ArrayList<Pokemon>();
        this.model = new DefaultTableModel() {

            private static final long serialVersionUID = 1L;

            Class<?>() types = new Class<?>() { String.class, String.class, String.class };
            boolean() editable = new boolean() { false, false, true };

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return this.types(columnIndex);
            }

            @Override
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return this.editable(columnIndex);
            }

        };

        JPanel panel = new JPanel(null);

        JLabel trainerLabel1 = new JLabel(name1 + "'s Pokémon");
        trainerLabel1.setBounds(margin, margin, 116, 30);
        panel.add(trainerLabel1);
        JTextField pokemonNameField1 = new HintTextField("name");
        pokemonNameField1.setBounds(trainerLabel1.getX() + trainerLabel1.getWidth(), margin, columns * 10, columns * 2);
        panel.add(pokemonNameField1);
        JLabel trainerLabel2 = new JLabel(name2 + "'s Pokémon");
        trainerLabel2.setBounds(margin, fontSize + margin * 2, 116, 30);
        panel.add(trainerLabel2);
        JTextField pokemonName2 = new HintTextField("name");
        pokemonName2.setBounds(trainerLabel2.getX() + trainerLabel2.getWidth(), fontSize + margin * 2, columns * 10, columns * 2);
        panel.add(pokemonName2);
        JTextField pokemonRoute = new HintTextField("route");
        pokemonRoute.setBounds(pokemonNameField1.getX() + pokemonNameField1.getWidth(), margin * 2, columns * 10, columns * 2);
        panel.add(pokemonRoute);

        JButton okButton = new JButton("OK");
        okButton.setBounds(pokemonRoute.getX() + pokemonRoute.getWidth() + margin / 4, pokemonRoute.getY() + pokemonRoute.getHeight() / 4, 48, pokemonRoute.getHeight() / 2);
        okButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                addToTable(new Pokemon(pokemonNameField1.getText().isBlank() ? Pokemon.NAMELESS : name1 + "'s " + pokemonNameField1.getText(), pokemonRoute.getText().isBlank() ? Pokemon.ROUTELESS : pokemonRoute.getText()));
                addToTable(new Pokemon(pokemonName2.getText().isBlank() ? Pokemon.NAMELESS : name2 + "'s " + pokemonName2.getText(), pokemonRoute.getText().isBlank() ? Pokemon.ROUTELESS : pokemonRoute.getText()));
                pokemonNameField1.setText("");
                pokemonName2.setText("");
                pokemonRoute.setText("");
            }
        });
        panel.add(okButton);

        JTable t = new JTable(model);

        t.setRowSelectionAllowed(true);
        t.setBounds(margin - 1, pokemonName2.getY() + pokemonName2.getHeight() + margin, WIDTH - (margin * 2), HEIGHT - 100 - margin);
        t.setDefaultRenderer(String.class, new CustomTableRenderer());
        t.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_DELETE || e.getKeyCode() == KeyEvent.VK_BACK_SPACE) removeFromTable(t);
            }
        });

        model.addColumn("Name");
        model.addColumn("Route");
        model.addColumn("Dead");

        t.setRowHeight(16);
        TableColumnModel columnModel = t.getColumnModel();
        columnModel.getColumn(0).setPreferredWidth(200);
        columnModel.getColumn(1).setPreferredWidth(100);
        columnModel.getColumn(2).setPreferredWidth(5);

        load("data.csv");

        JScrollPane sp = new JScrollPane(t);
        sp.setBounds(t.getX(), t.getY(), t.getWidth(), t.getHeight());
        panel.add(sp);

        JLabel icon = new JLabel(new ImageIcon(ImageIO.read(new File("ico.png")).getScaledInstance(64, 64, Image.SCALE_SMOOTH)));
        int x = okButton.getX() + okButton.getWidth() + margin;
        int y = margin / 2 + 2;
        icon.setBounds(x, y, WIDTH - x - margin, HEIGHT - t.getHeight() - margin * 4 + 1);
        panel.add(icon);

        setAllFont(panel, new Font("Menlo", Font.PLAIN, fontSize));

        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                save(t);
                System.exit(0);
            }
        });
        setTitle(TITLE);
        Dimension size = new Dimension(WIDTH, HEIGHT);
        getContentPane().setMinimumSize(size);
        getContentPane().setMaximumSize(size);
        getContentPane().setPreferredSize(size);
        getContentPane().add(panel);
        pack();
        setLocationByPlatform(true);
        setLocationRelativeTo(null);
        setResizable(false);
        setVisible(true);
    }

    public void load(String f) {
        try (BufferedReader br = Files.newBufferedReader(Paths.get(f), StandardCharsets.US_ASCII)) {
            String line = br.readLine();

            while (line != null) {
                String() attributes = line.split(",");
                String name = attributes(0);
                String route = attributes(1);
                boolean dead = (attributes(2).contains("false") ? false : true);

                addToTable(new Pokemon(name.substring(6, name.length()), route.substring(7, route.length()), dead));

                line = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void save(JTable t) {
        if (!isEmpty(t)) {
            Vector<?> row = (Vector<?>) model.getDataVector().elementAt(1);

            for (int i = 0; i < t.getRowCount(); i++) {
                row = (Vector<?>) model.getDataVector().elementAt(i);
                Pokemon p = pokemon.get(i);
                /** synchronizing list of pokemon with table representation */
                p.shoudlDie((row.get(2).toString().toLowerCase().equals("false") ? false : true));
            }
        }

        try (BufferedWriter writer = new BufferedWriter(new FileWriter("data.csv"))) {
            pokemon.forEach(pokemon -> {
                try {
                    writer.append(pokemon.toString() + "n");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    void addToTable(Pokemon p) {
        model.addRow(new String() { p.getName(), p.getRoute(), (p.isDead()) ? "true" : "false" });
        pokemon.add(p);
    }

    public static boolean isEmpty(JTable t) {
        if (t != null && t.getModel() != null) return t.getModel().getRowCount() <= 0 ? true : false;
        return false;
    }

    public void removeFromTable(JTable t) {
        int() rows = t.getSelectedRows();
        if (t.getSelectedRow() >= 0) pokemon.remove(t.getSelectedRow());
        for (int i = 0; i < rows.length; i++) model.removeRow(rows(i) - i);
    }

    public void setAllFont(Component c, Font f) {
        c.setFont(f);
        if (c instanceof Container) for (Component child : ((Container) c).getComponents()) {
            setAllFont(child, (child instanceof JButton) ? new Font("Menlo", Font.PLAIN, 11) : f);
        }
    }

    public static void main(String() args) throws IOException {
        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) if ("Metal".equals(info.getName())) {
                UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        } catch (Exception e) {
            try {
                UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                ex.printStackTrace();
            }
        }
        new PokeTracker();
    }

    class CustomTableRenderer extends DefaultTableCellRenderer {

        private static final long serialVersionUID = 1L;

        @Override
        public Component getTableCellRendererComponent(JTable t, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            Component component = super.getTableCellRendererComponent(t, value, isSelected, hasFocus, row, column);
            String data = t.getValueAt(row, 0).toString();

            if (data.contains(name1)) component.setForeground(Color.RED);
            else if (data.contains(name2)) component.setForeground(Color.BLUE);
            else component.setForeground(Color.BLACK);
            return component;
        }

    }

    /** https://stackoverflow.com/a/24571681 */
    class HintTextField extends JTextField {

        private static final long serialVersionUID = 1L;

        public HintTextField(String hint) {
            _hint = hint;
        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);
            if (getText().length() == 0) {
                int h = getHeight();
                ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
                Insets ins = getInsets();
                FontMetrics fm = g.getFontMetrics();
                int c0 = getBackground().getRGB();
                int c1 = getForeground().getRGB();
                int m = 0xfefefefe;
                int c2 = ((c0 & m) >>> 1) + ((c1 & m) >>> 1);
                g.setColor(new Color(c2, true));
                g.drawString(_hint, ins.left, h / 2 + fm.getAscent() / 2 - 2);
            }
        }

        private final String _hint;
    }

    class Pokemon {

        public static final String NAMELESS = "???";
        public static final String ROUTELESS = "???";

        private String name;
        private String route;
        private boolean dead;

        public Pokemon(String name, String route, boolean dead) {
            this.name = name;
            this.route = route;
            this.dead = dead;
        }

        public Pokemon(String name, String route) {
            this.name = name;
            this.route = route;
            this.dead = false;
        }

        public Pokemon(String name) {
            this.name = name;
            this.route = ROUTELESS;
            this.dead = false;

        }

        public Pokemon() {
            this.name = NAMELESS;
            this.route = ROUTELESS;
            this.dead = false;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getRoute() {
            return route;
        }

        public void setRoute(String route) {
            this.route = route;
        }

        public boolean isDead() {
            return dead;
        }

        public void shoudlDie(boolean dead) {
            this.dead = dead;
        }
        
        @Override
        public String toString() {
            return "name: " + name + ",route: " + route + ",dead: " + dead + ",";
        }

    }

}

Soul Link Info

ps: sorry for the ‘code evaluation’ instead of ‘code review’, I was not able to include the word ‘review’ in my title.

javascript – Poker hand evaluation

suits: (1, 1, 0, 3),
values: (0, 0, 0, 0, 1, 0, 2, 0, 1, 1, 0, 0, 0)

Given an array representing the suits and the values of the card, how do you numerically determine the value of your hand (5+2 best cards) in a game of Poker?

I am wondering how to do this, because you need to set a value for each card and also consider the suits, do you set a value to each index and then multiply the values inside the value array and then just add them up? And then how do you distinguish between a two pairs and a straight flush? Do you also have to multiply the final value with the value of each ranks (royal flush, straight flush, etc.)?

plotting – Limit Y-axis Evaluation but NOT the Plot Range

I have seen some answers to similar questions, but usually they are satisfied with just limiting the plot using PlotRange.
I want to keep the plot range as I have it, and just limit the range that is evaluated.
I don’t want to show that horizontal line at 90 (which is part of the output) so I want to limit the output to just above 90 (like 90.1) so that I can instead show the horizontal dashed line as the output approaches it asymptotically.
I think it does not tell the story as clearly if I just limit the range to 90 since it would be approaching the x-axis so I want to keep the displayed range down to 80.

ClearAll("Global`*")
eq1 = NSolve(0.5==(-(Pi)/3)r^3(1/Cos((Theta)*Degree)^3)(2+Sin((Theta)*Degree))(1-Sin((Theta)*Degree))^2, (Theta));
Labeled(Plot((Theta)/.eq1, {r,0.6203, 6} ,ImageSize->700,ImageMargins->0,ImagePadding->{{22, 10}, {15, 5}}, PlotRange-> {{0,6},{90,180}}, GridLines ->{{{0.62,Dashed}}, {{90, Dashed}}}, Ticks ->{{0,0.62,1,2,3,4,5,6},{10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180}}),{"Contact Angle ((Degree))","Tube Radius",  "Maximum Contact Angle for Plug of Volume 1 at Critical Bond Number" }, {Left, Bottom, Top}, RotateLabel -> True, Spacings ->{0,1,0})