Task: Honeycombs



C#: using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;







namespace Honeycombs
{
    public partial class MainWindow
    {
        private const int RowCount = 4;
        private const int ColCount = 5;
        private const int LabelSize = 20;
        private readonly char[] _permutedChars;

        public MainWindow()
        {
            if (RowCount * ColCount > 26)
#pragma warning disable 162
            {
                throw new ArgumentException("Too many cells");
            }
#pragma warning restore 162
            _permutedChars = GetPermutedChars(RowCount * ColCount);

            
            InitializeComponent();
        }

        private static char[] GetPermutedChars(int characterCount)
        {
            const string allChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            var rnd = new Random();
            var chars = new char[allChars.Length];

            for (int i = 0; i < allChars.Length; i++)
            {
                chars[i] = allChars[i];
            }

            for (int i = 0; i < characterCount; i++)
            {
                int swapIndex = rnd.Next() % (allChars.Length - i);
                char tmp = chars[swapIndex + i];
                chars[swapIndex + i] = chars[i];
                chars[i] = tmp;
            }
            return chars;
        }

        private void SetHexProperties(UIElementCollection hexes, double cellSize)
        {
            int charIndex = 0;
            List<Polygon> hexList = hexes.Cast<Polygon>().ToList();

            foreach (Polygon element in hexList)
            {
                SetHexProperties(element, _permutedChars[charIndex++], cellSize);
            }
        }

        private void SetHexProperties(Polygon hex, char charToSet, double cellSize)
        {
            var tag = (Tuple<int, int, double, double>) hex.Tag;
            double cellX = tag.Item3;
            double cellY = tag.Item4;

            
            

            var centeringGrid = new Grid();
            centeringGrid.Width = centeringGrid.Height = 2 * cellSize;
            centeringGrid.SetValue(Canvas.LeftProperty, cellX - cellSize);
            centeringGrid.SetValue(Canvas.TopProperty, cellY - cellSize);
            centeringGrid.IsHitTestVisible = false;
            HoneycombCanvas.Children.Add(centeringGrid);

            var label = new TextBlock
                {
                    Text = new string(charToSet, 1),
                    FontFamily = new FontFamily("Segoe"),
                    FontSize = LabelSize
                };
            label.HorizontalAlignment = HorizontalAlignment.Center;
            label.VerticalAlignment = VerticalAlignment.Center;
            label.IsHitTestVisible = false;
            centeringGrid.Children.Add(label);

            
            hex.Tag = charToSet;
            hex.Fill = new SolidColorBrush(Colors.Yellow);
            hex.Stroke = new SolidColorBrush(Colors.Black);
            hex.StrokeThickness = cellSize / 10;

            
            hex.MouseDown += hex_MouseDown;
        }

        private void hex_MouseDown(object sender, MouseButtonEventArgs e)
        {
            var hex = sender as Shape;
            if (hex == null)
            {
                throw new InvalidCastException("Non-shape in Honeycomb");
            }

            
            var ch = (char) hex.Tag;

            
            Letters.Text = Letters.Text + ch;

            
            hex.Fill = new SolidColorBrush(Colors.Magenta);

            
            hex.MouseDown -= hex_MouseDown;
        }

        private static void GetCombSize(double actualHeight, double actualWidth, int columns, int rows,
                                        out double cellSize, out double combHeight, out double combWidth)
        {
            double columnFactor = (3 * columns + 1) / 2.0;
            double rowFactor = (Math.Sqrt(3) * (2 * rows + 1)) / 2.0;
            double cellFromWidth = actualWidth / columnFactor;
            double cellFromHeight = actualHeight / rowFactor;
            cellSize = Math.Min(cellFromWidth, cellFromHeight);
            combWidth = cellSize * columnFactor;
            combHeight = cellSize * rowFactor;
        }

        private static void AddCells(Canvas canvas, double cellSize, int columns, int rows)
        {
            double rowHeight = cellSize * Math.Sqrt(3) / 2;

            for (int row = 0; row < rows; row++)
            {
                AddRow(rowHeight, canvas, cellSize, columns, row);
                rowHeight += cellSize * Math.Sqrt(3);
            }
        }

        private static void AddRow(double rowHeight, Canvas canvas, double cellSize, int columnCount, int row)
        {
            double cellX = cellSize;
            double cellHeight = cellSize * Math.Sqrt(3);

            for (int col = 0; col < columnCount; col++)
            {
                double cellY = rowHeight + ((col & 1) == 1 ? cellHeight / 2 : 0);
                Polygon hex = GetCenteredHex(cellSize, cellX, cellY, cellHeight);
                hex.Tag = Tuple.Create(col, row, cellX, cellY);
                canvas.Children.Add(hex);
                cellX += 3 * cellSize / 2;
            }
        }

        private static Polygon GetCenteredHex(double cellSize, double cellX, double cellY, double cellHeight)
        {
            var hex = new Polygon();
            hex.Points.Add(new Point(cellX - cellSize, cellY));
            hex.Points.Add(new Point(cellX - cellSize / 2, cellY + cellHeight / 2));
            hex.Points.Add(new Point(cellX + cellSize / 2, cellY + cellHeight / 2));
            hex.Points.Add(new Point(cellX + cellSize, cellY));
            hex.Points.Add(new Point(cellX + cellSize / 2, cellY - cellHeight / 2));
            hex.Points.Add(new Point(cellX - cellSize / 2, cellY - cellHeight / 2));
            return hex;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            double combHeight, combWidth, cellSize;

            
            GetCombSize(Main.ActualHeight, Main.ActualWidth, ColCount, RowCount, out cellSize, out combHeight,
                        out combWidth);

            
            HoneycombCanvas.Width = combWidth;
            HoneycombCanvas.Height = combHeight;

            
            AddCells(HoneycombCanvas, cellSize, ColCount, RowCount);

            
            SetHexProperties(HoneycombCanvas.Children, cellSize);
        }
    }
}



Java: import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Honeycombs extends JFrame {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame f = new Honeycombs();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
        });
    }

    public Honeycombs() {
        add(new HoneycombsPanel(), BorderLayout.CENTER);
        setTitle("Honeycombs");
        setResizable(false);
        pack();
        setLocationRelativeTo(null);
    }
}

class HoneycombsPanel extends JPanel {

    Hexagon[] comb;

    public HoneycombsPanel() {
        setPreferredSize(new Dimension(600, 500));
        setBackground(Color.white);
        setFocusable(true);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                for (Hexagon hex : comb)
                    if (hex.contains(e.getX(), e.getY())) {
                        hex.setSelected();
                        break;
                    }
                repaint();
            }
        });

        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                for (Hexagon hex : comb)
                    if (hex.letter == Character.toUpperCase(e.getKeyChar())) {
                        hex.setSelected();
                        break;
                    }
                repaint();
            }
        });

        char[] letters = "LRDGITPFBVOKANUYCESM".toCharArray();
        comb = new Hexagon[20];

        int x1 = 150, y1 = 100, x2 = 225, y2 = 143, w = 150, h = 87;
        for (int i = 0; i < comb.length; i++) {
            int x, y;
            if (i < 12) {
                x = x1 + (i % 3) * w;
                y = y1 + (i / 3) * h;
            } else {
                x = x2 + (i % 2) * w;
                y = y2 + ((i - 12) / 2) * h;
            }
            comb[i] = new Hexagon(x, y, w / 3, letters[i]);
        }

        requestFocus();
    }

    @Override
    public void paintComponent(Graphics gg) {
        super.paintComponent(gg);
        Graphics2D g = (Graphics2D) gg;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        g.setFont(new Font("SansSerif", Font.BOLD, 30));
        g.setStroke(new BasicStroke(3));

        for (Hexagon hex : comb)
            hex.draw(g);
    }
}

class Hexagon extends Polygon {
    final Color baseColor = Color.yellow;
    final Color selectedColor = Color.magenta;
    final char letter;

    private boolean hasBeenSelected;

    Hexagon(int x, int y, int halfWidth, char c) {
        letter = c;
        for (int i = 0; i < 6; i++)
            addPoint((int) (x + halfWidth * Math.cos(i * Math.PI / 3)),
                     (int) (y + halfWidth * Math.sin(i * Math.PI / 3)));
        getBounds();
    }

    void setSelected() {
        hasBeenSelected = true;
    }

    void draw(Graphics2D g) {
        g.setColor(hasBeenSelected ? selectedColor : baseColor);
        g.fillPolygon(this);

        g.setColor(Color.black);
        g.drawPolygon(this);

        g.setColor(hasBeenSelected ? Color.black : Color.red);
        drawCenteredString(g, String.valueOf(letter));
    }

    void drawCenteredString(Graphics2D g, String s) {
        FontMetrics fm = g.getFontMetrics();
        int asc = fm.getAscent();
        int dec = fm.getDescent();

        int x = bounds.x + (bounds.width - fm.stringWidth(s)) / 2;
        int y = bounds.y + (asc + (bounds.height - (asc + dec)) / 2);

        g.drawString(s, x, y);
    }
}