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);
        }
    }
}


Go: package main

import (
    rl "github.com/gen2brain/raylib-go/raylib"
    "math"
    "strings"
)

type hexagon struct {
    x, y     float32
    letter   rune
    selected bool
}

func (h hexagon) points(r float32) []rl.Vector2 {
    res := make([]rl.Vector2, 7)
    for i := 0; i < 7; i++ {
        fi := float64(i)
        res[i].X = h.x + r*float32(math.Cos(math.Pi*fi/3))
        res[i].Y = h.y + r*float32(math.Sin(math.Pi*fi/3))
    }
    return res
}

func inHexagon(pts []rl.Vector2, pt rl.Vector2) bool {
    rec := rl.NewRectangle(pts[4].X, pts[4].Y, pts[5].X-pts[4].X, pts[2].Y-pts[4].Y)
    if rl.CheckCollisionPointRec(pt, rec) {
        return true
    }
    if rl.CheckCollisionPointTriangle(pt, pts[2], pts[3], pts[4]) {
        return true
    }
    if rl.CheckCollisionPointTriangle(pt, pts[0], pts[1], pts[5]) {
        return true
    }
    return false
}

func DrawLineStrip(points []rl.Vector2, pointsCount int32, color rl.Color) {
    for i := int32(0);  i < pointsCount - 1; i++  {
         rl.DrawLineV(points[i], points[i+1], color)
    }
}

func main() {
    screenWidth := int32(600)
    screenHeight := int32(600)
    rl.InitWindow(screenWidth, screenHeight, "Honeycombs")
    rl.SetTargetFPS(60)

    letters := "LRDGITPFBVOKANUYCESM"
    runes := []rune(letters)
    var combs [20]hexagon
    var pts [20][]rl.Vector2

    x1, y1 := 150, 100
    x2, y2 := 225, 143
    w, h := 150, 87
    r := float32(w / 3)
    for i := 0; i < 20; i++ {
        var x, y int
        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
        }
        combs[i] = hexagon{float32(x), float32(y), runes[i], false}
        pts[i] = combs[i].points(r)
    }

    nChosen := 0
    sChosen := "Chosen: "
    lChosen := "Last chosen: "

    for !rl.WindowShouldClose() {
        rl.BeginDrawing()
        rl.ClearBackground(rl.RayWhite)
        for i, c := range combs {
            ctr := pts[i][0]
            ctr.X -= r
            index := -1
            if key := rl.GetKeyPressed(); key > 0 {
                if key >= 97 && key <= 122 {
                    key -= 32
                }
                index = strings.IndexRune(letters, key)
            } else if rl.IsMouseButtonPressed(rl.MouseLeftButton) {
                pt := rl.Vector2{float32(rl.GetMouseX()), float32(rl.GetMouseY())}
                for i := 0; i < 20; i++ {
                    if inHexagon(pts[i], pt) {
                        index = i
                        break
                    }
                }
            }
            if index >= 0 {
                if !combs[index].selected {
                    combs[index].selected = true
                    nChosen++
                    s := string(combs[index].letter)
                    sChosen += s
                    lChosen = "Last chosen: " + s
                    if nChosen == 20 {
                        lChosen += " (All 20 Chosen!)"
                    }
                }
            }
            if !c.selected {
                rl.DrawPoly(ctr, 6, r-1, 30, rl.Yellow)
            } else {
                rl.DrawPoly(ctr, 6, r-1, 30, rl.Magenta)
            }
            rl.DrawText(string(c.letter), int32(c.x)-5, int32(c.y)-10, 32, rl.Black)
            DrawLineStrip(pts[i], 7, rl.Black)
            rl.DrawText(sChosen, 100, 525, 24, rl.Black)
            rl.DrawText(lChosen, 100, 565, 24, rl.Black)
        }
        rl.EndDrawing()
    }

    rl.CloseWindow()
}