Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch

abra_joe
Hier soll von nun an ein Programmierspiel gestartet werden.

Es wird eine kleine Aufgabe vorgegeben, die der nachfolgende Poster zu bewältigen hat. Wie er die Problemlösung damit umsetzt, ist egal.
Hauptsache der Code liefert das gewünschte Ergebnis.

Derjenige, der die Aufgabe am schnellsten löst, darf dann eine eigene stellen.

Regeln:
Lediglich kleine Aufgaben, keine "Projekte" wie: Programmiere ein Gästebuch.
Jegliche Quellen müssen angegeben werden. Wer kann, sollte jedoch darauf verzichten.
Ein schön eingerückter Code, sodass es anderen Usern möglich ist, zu sehen, wie man an das Ergebnis gekommen ist.

Also hier mal die erste Aufgabe:

Eine Pyramide soll je nach Benutzereingabe erstellt werden.
Diese Benutzereingabe soll die Basis für die Pyramide sein:
Benutzereingabe "H":


A
ABA
ABCBA
ABCDCBA
ABCDEDCBA
ABCDEFEDCBA
ABCDEFGFEDCBA
ABCDEFGHGFEDCBA

Viel spaß
zommi
Also ich find die Idee doch nett.

C#-Code:
static void Main(string[] args)
{
    int end = Console.ReadKey().KeyChar - 'A';
    char[] s = new String(' ', end*2+1).ToCharArray();
    Console.Clear();
    for (int c = 0; c <= end; c++)
    {
        s[end + c] = s[end - c] = (char)('A' + c);
        Console.WriteLine(new String(s));
    }
}

//EDIT: DAMN! Die Pyramide ist genau falschrum ! *gruml* :D


Ich find den schon relativ elegant. Vielleicht geht aber noch besser ? (=kürzer, schöner, cooler)

Ich versuch mal was auszudenken (kann aber etwas dauern ;) )

beste Grüße
zommi
dN!3L
Zitat von zommi:
Vielleicht geht aber noch besser ? (=kürzer, schöner, cooler)

Hehe:

C#-Code:
private static string foo(char target)
{
    return String.Join("\r\n",
             Enumerable
                 .Range(1,target-'A'+1)
                 .Select(count => Enumerable
                     .Range('A',count)
                     .Select(index => (char)index))
                 .Select(sequence => sequence.Concat(sequence.Reverse().Skip(1)))
                 .Select(sequence => "".PadLeft(target-'A'-sequence.Count()/2)+new String(sequence.ToArray()))
                 .ToArray());
}

Gruß,
dN!3L
dN!3L
Zitat von abra_joe:
dN!3L darf die nächste Aufgabe stellen

Hm... also:

Eine Zahl soll in eine Zeichenkettenrepräsentation überführt werden, die diese Zahl in römischen Ziffern darstellt.
Einschränkungen: Ziffern I bis M. Subtraktionsregel.
Beispiel: 1984 -> MCMLXXXIV
edsplash
C#-Code:
static void Main(string[] args)
    {
      int originalNumber = 1984;
      StringBuilder romNumber = new StringBuilder();

      Queue<KeyValuePair<string, int>> queue = new Queue<KeyValuePair<string, int>>();
      queue.Enqueue(new KeyValuePair<string, int>("M", 1000));
      queue.Enqueue(new KeyValuePair<string, int>("D", 500));
      queue.Enqueue(new KeyValuePair<string, int>("C", 100));
      queue.Enqueue(new KeyValuePair<string, int>("L", 50));
      queue.Enqueue(new KeyValuePair<string, int>("X", 10));
      queue.Enqueue(new KeyValuePair<string, int>("V", 5));
      queue.Enqueue(new KeyValuePair<string, int>("I", 1));

      foreach (KeyValuePair<string, int> pair in queue)
      {
        int times = (int)Math.Round((double)(originalNumber / pair.Value), 0);
        originalNumber -= pair.Value * times;

        for (int k = 0; k < times; k++)
        {
          romNumber.Append(pair.Key);
        }
      }

      Console.WriteLine(romNumber.ToString());
    }

[edit] Mist, hab ich doch glatt das mit der Subtraktionsregel übersehen
dN!3L


Zitat von zommi:
//EDIT: DAMN! Die Pyramide ist genau falschrum!

Zitat von edsplash:
[edit] Mist, hab ich doch glatt das mit der Subtraktionsregel übersehen

Hehe, das wäre hier doch der ideale Platz, um mal TDD zu üben.
Der Fragensteller gibt zur Aufgabenstellung ein Interface und einen Test (oder mehrere vor), die bestanden werden müssen... großes Grinsen
edsplash
Zitat von dN!3L:

Hehe, das wäre hier doch der ideale Platz, um mal TDD zu üben.

Da kann man sich mal über die Effizienz amüsieren ;) Zwei Aufgaben und zweimal in der ersten Lösung die gepostet wurde ein elementarer Fehler, der mit einer Überprüfung der Aufgabenstellung vor dem Post hätte gesehen werden können.

Nichtsdestotrotz meine endgültige Lösung:

C#-Code:
private static string DecimalToRoman(int originalNumber)
    {
      string romanNumber = string.Empty;

      Dictionary<int, char> digits = new Dictionary<int, char>();
      digits.Add(1000, 'M');
      digits.Add(500, 'D');
      digits.Add(100, 'C');
      digits.Add(50, 'L');
      digits.Add(10, 'X');
      digits.Add(5, 'V');
      digits.Add(1, 'I');

      int[] decades = new int[4];
      decades[0] = originalNumber % 10;
      decades[1] = originalNumber % 100 - decades[0];
      decades[2] = originalNumber % 1000 - decades[1] - decades[0];
      decades[3] = originalNumber % 10000 - decades[2] - decades[1] - decades[0];

      for (int k = 3; k >= 0; k--)
      {
        int decadeValue = decades[k];
        int decadeFloor = (int)Math.Pow(10, k);
        int times = decadeValue / decadeFloor;
        Action standardConvert = () =>
        {
          for (int i = 0; i < times; i++)
            romanNumber += digits[decadeFloor];
        };

        if (times >= 5)
        {
          if (times == 9)
          {
            romanNumber += digits[decadeFloor];
            romanNumber += digits[10 * decadeFloor];
          }
          else
          {
            decadeValue -= 5 * decadeFloor;
            romanNumber += digits[5 * decadeFloor];
            times = decadeValue / decadeFloor;
            standardConvert();
          }
        }
        else
        {
          if (times == 4)
          {
            if (decadeFloor == 1000)
              standardConvert();
            else
            {
              romanNumber += digits[decadeFloor];
              romanNumber += digits[5 * decadeFloor];
            }
          }
          else
            standardConvert();
        }
      }
      return romanNumber;
    }

Wäre wohl mit weniger Code gegangen, aber ist das was mir auf die Schnelle so gelungen ist ;)

EDIT: ujr hat mich auf einen 'kleinen' Bug in meiner Lösung aufmerksam gemacht, welchen ich mittlerweile beseitigt habe. Das alte Beispiel mit dem Fehler wurde durch ein neues ersetzt ;)
edsplash
Hallo Zusammen

Hier meine Aufgabe:

Ziel ist folgendes Konstrukt dynamisch auf der Konsole auszugeben

Code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
#
##
# #
####
#   #
##  ##
# # # #
########
#       #
##      ##
# #     # #
####    ####
#   #   #   #
##  ##  ##  ##
# # # # # # # #
################
#               #
##              ##
# #             # #
####            ####
#   #           #   #
##  ##          ##  ##
# # # #         # # # #
########        ########
#       #       #       #
##      ##      ##      ##
# #     # #     # #     # #
####    ####    ####    ####
#   #   #   #   #   #   #   #
##  ##  ##  ##  ##  ##  ##  ##
# # # # # # # # # # # # # # # #
################################

Die gesetzten Pixel/Buchstaben ergeben sich jeweils aus dem direkt über diesem liegendem Feld und dem links darüberligendem.
Die Linke obere Ecke ist als Ausgangspunkt bekannt.

Viel Spass und Vielen Dank an Floste für den Tipp
michlG
Hallo Leute,

hier die Lösung dazu

C#-Code:
    private static void PrintTriangles(int count)
    {
      bool[,] arr = new bool[count,count];
      arr[0, 0] = true;
      Console.WriteLine("#");

      for (int i = 1; i < count; i++)
      {
        arr[i, 0] = true;
        Console.Write("#");
        for (int j = 1; j < count; j++)
        {
          arr[i, j] = arr[i - 1, j] ^ arr[i - 1, j - 1];
          Console.Write(arr[i,j] ? "#" : " ");
        }
        Console.WriteLine();
      }
    }

Einfach PrintTriangles(32) aufrufen dann gibts die geforderte Ausgabe

Die neue Aufgabe folgt gleich

Gruss
Michael
michlG
Hallo,

hier die neue Aufgabe.

Erstellt einen sog. Tromino Tiling Algorithmus.
Der ein 2^n mal 2^n großen Array mit den einzelnen Trominos aufteilt.

Ein Tromino wäre also sowas

Code:
1:
2:
3:
4:
0000
0220
0210
0000

Am Anfang muss ein Loch (1) an einer beliebigen Position gesetzt werden.
Dann wird die ganze Fläche schön mit den Trominos ausgefüllt.
Gestartet wird mit der 2, weiter gehts mit der 3 usw. Am Ende muss es halt schön foll sein.

Am Bild seht ihr wie der Array zu beginn aussieht. Also nur das Loch (1) ist bekannt.
Am Ende muss es dann so wie rechts gezeigt ausgegeben werden

Die Ausgabe sollte in der Konsole sein (wenn sich das Zeug aufgrund der unterschiedlichen Zeichenlänge nicht zusehr verschiebt).

Viel Spass damit

Gruss
Michael
trib
Da ich überhaupt nicht verstanden habe was MichlG mir mit den Trominos sagen wollte, habe ich mal Google konsultiert und eine grafische Erklärung gefunden.
Die wollte ich euch nicht vorenthalten:

 http://oneweb.utc.edu/~Christopher-Mawata/trominos/

Gruß
TriB
Floste
Ganz schön schwer, wenn man sich selbst was ausdenkt.
Ich hab einfach mal brute force try und error gemacht, braucht ewig für n>=5, aber die in dem bild da sind eh nur n=3:

C#-Code:
public static void Main()
        {
            while (true)
            {
                int xpos, ypos, n;
                do { Console.Write("X="); }
                while (!int.TryParse(Console.ReadLine(), out xpos));
                do Console.Write("Y=");
                while (!int.TryParse(Console.ReadLine(), out ypos));
                do Console.Write("N=");
                while (!int.TryParse(Console.ReadLine(), out n));
                int num = 1 << (byte)n;
                int[,] feld = new int[num, num];
                feld[xpos, ypos] = -1;
                if (!BruteForce(feld, num, 1))
                {
                    Console.WriteLine("Nicht gelöst");
                }
                else
                {
                    for (int y = 0; y < num; y++)
                    {
                        for (int x = 0; x < num; x++)
                        {
                            Console.Write(feld[x, y].ToString().PadRight(3));
                        }
                        Console.WriteLine();
                        Console.WriteLine();
                    }
                }
            }
        }


        private static bool BruteForce(int[,] feld, int num, int tiefe)
        {
            for (int y = 0; y < num; y++)
                for (int x = 0; x < num; x++)
                    if (feld[x, y] == 0)
                    {
                        Point[] points = new Point[3];
                        for (int square = 0; square < 4; square++)
                        {
                            int sx = (square & 1) + x - 1;
                            int sy = ((square & 2) >> 1) + y - 1;
                            for (int ep = 0; ep < 4; ep++)
                            {
                                int i = 0;
                                for (int p = 0; p < 4; p++)
                                {
                                    if (p != ep)
                                        points[i++] = new Point(
                                            (p & 1) + sx,
                                            ((p & 2) >> 1) + sy);
                                    else if (((p & 1) + sx == x) && (((p & 2) >> 1) + sy) == y) goto bad_ep;
                                }
                                if (TrominoTesten(feld, num, tiefe, points)) return true;
                            bad_ep: { }
                            }
                        }
                        return false;
                    }
            return true;
        }

        private static bool TrominoTesten(int[,] feld, int num, int tiefe, IEnumerable<Point> points)
        {
            foreach (Point p in points)
            {
                if (p.X < 0 || p.X >= num) return false;
                if (p.Y < 0 || p.Y >= num) return false;
                if (feld[p.X, p.Y] != 0) return false;
            }
            foreach (Point p in points)
                feld[p.X, p.Y] = tiefe;
            if (BruteForce(feld, num, tiefe+1)) return true;
            foreach (Point p in points)
                    feld[p.X, p.Y] = 0;
            return false;
        }
Th69
Unter  http://www.projektwoche.jku.at/2004/p4_1.pdf ist sowohl der Beweis als auch ein effizienter Algorithmus für das Tromino-Problem angegeben (2. Problem).
zommi
Jea, ich habs!
Hab Floste Programmrahmen als Basis genommen.

C#-Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace Tromino
{
    class Program
    {
        public static void Main()
        {
            while (true)
            {
                int xpos, ypos, n;
                do { Console.Write("X="); }
                while (!int.TryParse(Console.ReadLine(), out xpos));
                do Console.Write("Y=");
                while (!int.TryParse(Console.ReadLine(), out ypos));
                do Console.Write("N=");
                while (!int.TryParse(Console.ReadLine(), out n));
                int num = 1 << (byte)n;
                int[,] feld = new int[num, num];
                feld[xpos, ypos] = -1;

                globalCount = 0;
                tile(feld, new Point(xpos, ypos));

                char[,] res = renderOutlineOfField(feld);

                res[xpos * 2 + 1, ypos * 2 + 1] = 'X';

                {
                    for (int y = 0; y < res.GetLength(0); y++)
                    {
                        for (int x = 0; x < res.GetLength(1); x++)
                        {
                            Console.Write(res[x, y]);
                        }
                        //Console.WriteLine();
                        Console.WriteLine();
                    }
                }
            }
        }


        static int globalCount = 0;

        private static void tile(int[,] feld, Point hole)
        {
            tile(feld, Point.Empty, hole, new Size(feld.GetLength(0), feld.GetLength(1)));
        }

        // *********************************************************
        // *********************************************************
        // *********************************************************
        // Kern des Algos (gemäß Divide and Conquer)
        private static void tile(int[,] feld, Point fieldStart, Point hole, Size size)
        {
            int trominoSymbol = globalCount++;

            // Rekursionsabbruch bei trivialem Fall (Conquer)
            if (size == new Size(2, 2))
            {
                for (int x = fieldStart.X; x < (fieldStart + size).X; x++)
                    for (int y = fieldStart.Y; y < (fieldStart + size).Y; y++)
                    {
                        if (!(x == hole.X && y == hole.Y))
                        {
                            feld[x, y] = trominoSymbol;
                        }
                    }
                return;
            }

            // Divide-Schritt (in 4 Unterprobleme)
            Size halfSize = new Size(size.Width / 2, size.Height / 2);
            for (int i = 0; i < 2; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    Point subFieldStart = fieldStart + new Size(halfSize.Width * i, halfSize.Height * j);
                    Point newHole = fieldStart + halfSize - new Size(1 - i, 1 - j);
                    if (new Rectangle(subFieldStart, halfSize).Contains(hole))
                    {
                        newHole = hole;
                    }
                    else
                    {
                        feld[newHole.X, newHole.Y] = trominoSymbol;
                    }
                    tile(feld, subFieldStart, newHole, halfSize);
                }
            }
        }
        // *********************************************************
        // *********************************************************
        // *********************************************************


        // Diese Method ist nur Deko, macht was hübsches, ansehliches draus
        static char[,] renderOutlineOfField(int[,] feld)
        {
            char[,] res = new char[feld.GetLength(0) * 2 + 1, feld.GetLength(1) * 2 + 1];
            //rand
            for (int x = 1; x < res.GetLength(0) - 1; x++)
                res[x, 0] = res[x, res.GetLength(1) - 1] = symbols[10];
            for (int y = 1; y < res.GetLength(1) - 1; y++)
                res[0, y] = res[res.GetLength(0) - 1, y] = symbols[5];
            //ecken
            res[0, 0] = symbols[6];
            res[res.GetLength(0) - 1, 0] = symbols[12];
            res[0, res.GetLength(1) - 1] = symbols[3];
            res[res.GetLength(0) - 1, res.GetLength(1) - 1] = symbols[9];

            for (int x = 1; x < res.GetLength(0) - 1; x++)
            {
                for (int y = 1; y < res.GetLength(1) - 1; y++)
                {
                    int selection = 0;
                    if (feld[(x - 1) / 2, (y - 1) / 2] != feld[(x - 0) / 2, (y - 1) / 2])
                        selection |= 1;
                    if (feld[(x - 0) / 2, (y - 1) / 2] != feld[(x - 0) / 2, (y - 0) / 2])
                        selection |= 2;
                    if (feld[(x - 1) / 2, (y - 0) / 2] != feld[(x - 0) / 2, (y - 0) / 2])
                        selection |= 4;
                    if (feld[(x - 1) / 2, (y - 1) / 2] != feld[(x - 1) / 2, (y - 0) / 2])
                        selection |= 8;
                    res[x, y] = symbols[selection];
                }
            }
            return res;
        }

        static char[] symbols = new char[] { ' ', '?', '?', '\u2514', '?', '\u2502', '\u250C', '\u251C', '?', '\u2518', '\u2500', '\u2534', '\u2510', '\u2524', '\u252C', '\u253C' };

    }
}

(benötigt wegen dem Drawing-Namespace nochn Verweis auf System.Drawing)

beste Grüße
zommi
Corpsegrinder
Ja dann hau ma die nächste Aufgabe rein :D
zommi
Also meine Aufgabe:

programmiere eine einzige Methode f(n) zum Berechnen der Quadratzahl n^2.
Allerdings sind * und / nicht zugelassen. Was als arithmetische Operation zugelassen ist, sind: + und - .
Ahso und lokale/temporäre Variablen sind natürlich auch nich zugelassen ;)

beste Grüße
zommi
Corpsegrinder
Zitat von zommi:
Also meine Aufgabe:

programmiere eine einzige Methode f(n) zum Berechnen der Quadratzahl n^2.
Allerdings sind * und / nicht zugelassen. Was als arithmetische Operation zugelassen ist, sind: + und - .
Ahso und lokale/temporäre Variablen sind natürlich auch nich zugelassen ;)

beste Grüße
zommi

Also ich bezweifel, dass es mit nur einer Methode möglich ist ;-) du musst ja irgendwie ne Laufvariable und ne tmp mitgeben. Hab das aber mal in Scala gelöst mit nur "einer" Methode :D

Code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
def square(value: Int) = {
    def sq(value: Int, tmp: Int, count: Int): Int = {
      if(count == 0)
        tmp
      else
        sq(value, tmp + value, count-1)
    }
    sq(value, 0, value)
  }
zommi
Zitat:
Also ich bezweifel, dass es mit nur einer Methode möglich ist ;-)

Mit nur einer Methode!
Und mit keiner zusätzlichen Variable.
Und ohneMultiplikation.

cool

beste Grüße
zommi
Corpsegrinder
Okay... wieder was gelernt... den Weg kannte ich noch nicht :-)

C#-Code:
public static int square(int val)
{
  if (val == 1)
    return 1;
  else
    return val + (val - 1) + square(val - 1);
}

Achso... Quelle:  http://en.wikipedia.org/wiki/Square_(algebra)
zommi
Jup, perfekt Daumen hoch

(wobei ich bei n=0 die Abbruch-Bedingung gemacht hätte)
Ich lös es noch mit kurzer Erklärung auf:

Berechnung per Rekursion! Wir nutzen aus:

(n+1)² = n² + 2n + 1

Und mit n statt (n+1) ergibt sich dann

n² = (n-1)² + 2*(n-1) + 1
n² = (n-1)² + 2*n - 1
n² = (n-1)² + n + n - 1

Dann noch bei n=0 die Abbruchbedingung mit 0²=0 und fertig ist die Methode.
Als Einzeiler:

C#-Code:
square(int n)
{
     return (n==0)?0:square(n-1) + n + n - 1;
}

beste Grüße
zommi