C# :: Aufgabe #174 :: Lösung #1

3 Lösungen Lösungen öffentlich
#174

Crowdfunding für Fibonacci-Uhr

Fortgeschrittener - C# von hollst - 24.04.2017 um 12:22 Uhr
Erneut FIBONACCI, jedoch diesmal in einem ganz anderen Zusammenhang.

Im Zuge von Crowdfunding ist in Kanada die sogenannte Fibonacci-Clock (Bild 1)
entwickelt und in die Produktion überführt worden.

(https://www.kickstarter.com/projects/basbrun/fibonacci-clock-an-open-source-clock-for-nerds-wit)

Es sollten für dieses Projekt innerhalb eines Monates (05 - 06/2015) 5.000 CAD
gesammelt werden (ca. 3.500 €), zusammengekommen sind aber sage und schreibe
(über) unglaubliche 125.000 €. Man lernt daraus, dass man mit einer guten (verrückten)
Idee manchmal auch einiges an Geld aquirieren kann.

Dabei ist an der Fibonacci-Clock nichts Kompliziertes dran. Im Grunde besteht sie
lediglich aus einem Display, das in fünf Quadrate mit unterschiedlicher
(variabler) Farbe und (fixer) Kantenlänge aufgeteilt ist.

Die Kantenlängen der Quadrate sind 1, 1, 2, 3 und 5, entsprechen also dem Beginn der
Fibonacci-Sequenz. Es lassen sich damit die Dezimalzahlen 0 ... 12 codieren.

Das Interessante an dem Display ist, dass man damit gleichzeitig
die Stunden und (teilweise) die Minuten darstellen kann. Dies geschieht mittels
wechselnder Farbcodierung der Quadrate und Aufaddition der den Quadraten zugeordnete
Fibonaccizahl. Die Farbe Rot steht für die Stunden, die Farbe Grün für die Minuten.
Wird ein Quadrat sowohl für die Stunden- als auch für die Minutensummation benötigt,
wird dies durch die Farbe Blau angezeigt (Bild 2). Weiß steht für "Nichtnutzung" des
Quadrates. Die Minutenzahl erhält man durch Multiplikation der Minutenzahlsumme mit 5,
somit ist die Genauigkeit der erfindungsgemäßen Uhr auf +- 2.5 Minuten begrenzt,
Vor- und Nachmittagszeiten sind identisch.

Mit den fünf Fibonaccizahlen lassen sich wie gesagt alle Werte von 0 bis 12 darstellen,
d. h. die Minutenanzeige "springt" lediglich alle fünf Minuten. Dies bedeutet allerdings nicht,
dass die Anzeige nicht schneller wechseln darf, denn es gibt mit den Ausnahmen für die
Null und die Zwölf für jede Zahl mehrere Möglichkeiten der Fibonacci-Codierung, die der
kanadische Erfinder auch in kurzen Intervallen und zufällig wechseln läßt.


Die Programmieraufgabe besteht darin, die Fibonacci-Clock mit einer Grafik-Applikation zu
simulieren. Der Minutenwechsel soll allerdings nicht im 5-Minuten-Takt, sondern sogar minütlich
erfolgen. Damit könnte man die Uhr vielleicht als Tester für Schnelldenker einsetzen.

Neben den vier Grundfarben des Erfinders (Weiß, Rot, Grün und Blau (Bild 3))werden also
vier weitere Farben zur Kodierung der fehlenden Additivzahlen 1, 2, 3 und 4 für den Minutenzähler
benötigt. Im Beispiel (Bild 4) sind es die Farben

Yellow (steht für Überlagerung mit Weiß),
Pink (steht für Überlagerung mit Rot),
Purple (steht für Überlagerung mit Grün) und
SkyBlue (steht für Überlagerung mit Blau)

Viel Erfolg!

#1
2 Kommentare
1x
vote_ok
von daniel59 (4260 Punkte) - 28.04.2017 um 15:16 Uhr
MainWindow.xaml.cs
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;

namespace WpfFibonacciClock
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        static readonly int[] fibonacci = { 1, 1, 2, 3, 5 };
        static readonly int[] indexes = { 0, 1, 2, 3, 4 };
        static readonly Dictionary<int, List<int[]>> Combinations = GetCombinations();
        static readonly Random rnd = new Random();

        static readonly BrushConverter bc = new BrushConverter();
        static readonly Brush Green = (Brush)bc.ConvertFromString("#00ff00");

        DispatcherTimer timer;
        public MainWindow()
        {
            InitializeComponent();

            DateTime dt = DateTime.Now;
            ChangeClock(dt);
            timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, 60);
            timer.Tick += Timer_Tick;
            timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            DateTime dt = DateTime.Now;
            ChangeClock(dt);
        }

        private void ChangeClock(DateTime dt)
        {
            int hour = dt.Hour % 12;
            int min5 = dt.Minute / 5;
            int min1 = dt.Minute % 5;

            int tempH = rnd.Next(0, Combinations[hour].Count);
            int tempM5 = rnd.Next(0, Combinations[min5].Count);
            int tempM1 = rnd.Next(0, Combinations[min1].Count);

            int[] fieldsHour = Combinations[hour][tempH];
            int[] fieldsMin5 = Combinations[min5][tempM5];
            int[] fieldsMin1 = Combinations[min1][tempM1];

            var off = indexes.Except(fieldsHour).Except(fieldsMin5);//weiß
            var onHM = fieldsHour.Intersect(fieldsMin5);//blau
            var onH = fieldsHour.Except(onHM);//rot
            var onM5 = fieldsMin5.Except(onHM);//grün

            foreach (int i in off)
            {
                if (!fieldsMin1.Contains(i) || min1 == 0)
                { SetColor(i,Brushes.White); }
                else
                { SetColor(i, Brushes.Yellow); }
            }
            foreach (int i in onHM)
            {
                if (!fieldsMin1.Contains(i) || min1 == 0)
                { SetColor(i, Brushes.Blue); }
                else
                { SetColor(i, Brushes.SkyBlue); }
            }
            foreach (int i in onM5)
            {
                if (!fieldsMin1.Contains(i) || min1 == 0)
                { SetColor(i, Green); }
                else
                { SetColor(i, Brushes.Purple); }
            }
            foreach (int i in onH)
            {
                if (!fieldsMin1.Contains(i) || min1 == 0)
                { SetColor(i, Brushes.Red); }
                else
                { SetColor(i, Brushes.Pink); }
            }


        }

        private void SetColor(int field, Brush color)
        {
            switch (field)
            {
                case 0:
                    rectanglef0.Fill = color;
                    break;

                case 1:
                    rectanglef1.Fill = color;
                    break;

                case 2:
                    rectanglef2.Fill = color;
                    break;

                case 3:
                    rectanglef3.Fill = color;
                    break;

                case 4:
                    rectanglef4.Fill = color;
                    break;
            }
        }

        private static Dictionary<int, List<int[]>> GetCombinations()
        {
            Dictionary<int, List<int[]>> result = new Dictionary<int, List<int[]>>();
            result.Add(0, new List<int[]>() { new int[0] });
            new OrderLoop<int>(indexes, delegate (int[] array)
            {
                for (int l = 1; l <= array.Length; l++)
                {
                    int sum = 0;
                    int[] idx = new int[l];
                    for (int i = 0; i < l; i++)
                    {
                        sum += fibonacci[array[i]];
                        idx[i] = array[i];
                    }
                    if (sum == 12)
                    { sum = 0; }

                    if (!result.ContainsKey(sum))
                    {
                        result.Add(sum, new List<int[]>());
                    }
                    Array.Sort(idx);
                    if (!result[sum].Any(a => a.SequenceEqual(idx)))
                    { result[sum].Add(idx); }

                }
                return LoopKey.Continue;
            });

            return result;
        }
    }
}


MainWindow.xaml
Quellcode ausblenden XML-Code
<Window x:Class="WpfFibonacciClock.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfFibonacciClock"
        mc:Ignorable="d"
        Title="MainWindow" Height="1000" Width="1600" AllowsTransparency="True" WindowStyle="None" Background="Transparent" WindowStartupLocation="CenterScreen">
    <Grid>
        <Canvas x:Name="canvasClock">
            <!--Kantenlänge 1-->
            <Rectangle x:Name="rectanglef0" Width="200" Height="200" Stroke="Black" Fill="#ff0000" Canvas.Top="200" Canvas.Left="400"/>
            <!--Kantenlänge 1-->
            <Rectangle x:Name="rectanglef1" Width="200" Height="200" Stroke="Black" Fill="#ffffff" Canvas.Top="0" Canvas.Left="400"/>
            <!--Kantenlänge 2-->
            <Rectangle x:Name="rectanglef2" Width="400" Height="400" Stroke="Black" Fill="#00ff00" Canvas.Top="0" Canvas.Left="0"/>
            <!--Kantenlänge 3-->
            <Rectangle x:Name="rectanglef3" Width="600" Height="600" Stroke="Black" Fill="#0000ff" Canvas.Top="400" Canvas.Left="0"/>
            <!--Kantenlänge 5-->
            <Rectangle x:Name="rectanglef4" Width="1000" Height="1000" Stroke="Black" Fill="#ff0000" Canvas.Top="0" Canvas.Left="600"/>
        </Canvas>
    </Grid>
</Window>


OrderLoop.cs
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;

namespace WpfFibonacciClock
{
    public delegate T2 PFunc<in T1, out T2>(params T1[] parameter);
    public enum LoopKey { Break, Continue }
    public sealed class OrderLoop<T>
    {
        private int[] Indexes;
        private T[] Parameter;
        private int currentDepth = 1;
        private int continues = 1;

        private int count = 0;
        private T[] Source;
        private PFunc<T, LoopKey> Action;
        private bool breakUp = false;

        public OrderLoop(IEnumerable<T> source, PFunc<T, LoopKey> action)
        {
            Source = source.ToArray();
            Action = action;
            count = Source.Length;
            Parameter = new T[count];
            Indexes = new int[count];
            for (int i = 0; i < count; i++)
            { Indexes[i] = -1; }
            Invoke();
        }

        private void Invoke()
        {
            for (int i = 0; i < count && !breakUp; i++)
            {
                Parameter[currentDepth - 1] = Source[i];

                if (Indexes.Where(a => Array.IndexOf(Indexes, a) < currentDepth - 1).Contains(i))
                {
                    continue;
                }
                Indexes[currentDepth - 1] = i;
                if (currentDepth == count)
                {
                    LoopKey key = Action.Invoke(Parameter);
                    if (key == LoopKey.Break)
                    {
                        breakUp = true;
                        break;
                    }
                    else if ((int)key > 1)
                    {
                        continues = (int)key - 1;
                        if (continues < count)
                        {
                            goto DepthMinus;
                        }
                    }
                }
                else
                {
                    continues = ++currentDepth;
                    Invoke();
                    if (continues < currentDepth)
                    {
                        goto DepthMinus;
                    }
                }
            }
            DepthMinus:
            currentDepth--;
        }
    }
}


Kommentare:

hollst

Punkte: 13980

761 Aufgaben
132 Lösungen
117 Kommentare

#1
02.05.2017 um 16:25 Uhr
Sehr schön,

natürlich solltest Du noch ein Möglichkeit einbauen, wie man die Uhr auch ausschalten kann (außer über Task-Leiste oder gar Task-Manager).
post_arrow
444 0

daniel59

Punkte: 4260

10 Aufgaben
77 Lösungen
5 Kommentare

#2
05.05.2017 um 15:55 Uhr
Als Desktopanwendung würde ein Button zum Beenden oder der Rahmen Sinn machen. Ich hab dies bewusst weggelassen, denn so kann man das Programm auch schön als Bildschirmschoner nutzen ;)
post_arrow
445 0
Bitte melden Sie sich an um eine Kommentar zu schreiben.
Kommentar schreiben