Wyzwanie xkcd: „Procent ekranu w kolorze [x]”


57

Myślę więc, że prawdopodobnie wszyscy widzieliśmy ten komiks xkcd :

http://imgs.xkcd.com/comics/self_description.png:

To może być zbyt ogólne lub zbyt trudne, nie jestem pewien. Ale wyzwaniem jest stworzenie programu w dowolnym języku, który tworzy okno, które ma co najmniej 2 kolory i wyświetla w języku angielskim, jaki procent ekranu jest w każdym kolorze.

dawny. Najprostszym rozwiązaniem byłoby białe tło z czarnymi literami z napisem „Procent tego obrazu, który jest czarny: [x]%. Procent tego obrazu, który jest biały: [y]%”

Możesz zwariować lub tak prosto, jak chcesz; zwykły tekst jest poprawnym rozwiązaniem, ale jeśli robisz ciekawe obrazy, takie jak w komiksie xkcd, to nawet lepiej! Zwycięzca będzie najbardziej zabawnym i kreatywnym rozwiązaniem, które uzyska najwięcej głosów. Więc idź i stwórz coś zabawnego i godnego xkcd! :)

Więc co o tym myślisz? Brzmi jak zabawne wyzwanie? :)

W odpowiedzi umieść zrzut ekranu swojego programu :)


6
Program „Ten program ma 64 A, 4 B, ... i 34 podwójne cudzysłowy w kodzie źródłowym” byłby bardziej interesujący :-)
John Dvorak

2
OK ... jakie są obiektywne kryteria wygranej? Jak ustalić, czy jakieś dane wyjściowe są prawidłowe? Czy wystarczy, że jest to prawda i opisuje liczbowo swoją własność?
John Dvorak,

@JanDvorak Oh, to jest dobre! Program alfabetyczny tak naprawdę sprawił, że pomyślałem o tym pierwotnie, ale nie rozważałem dodania do niego elementu kodu źródłowego! Powinieneś opublikować to jako pytanie :) Tak, wystarczy, że jest to prawda i opisuje się. Hmm, masz rację, nie myślałem o tym, jak udowodnię, że ostateczne wyniki były prawidłowe. Potrzebuję sposobu, aby policzyć wszystkie piksele każdego koloru na obrazie wynikowym, jak sądzę. Pójdę teraz to zbadać. (Przepraszam, że moje pierwsze pytanie miało problemy ... Próbowałem, ale jestem nowy! Dziękuję :))
WendiKidd

jeśli prawdomówność i odniesienie do siebie są wystarczającymi kryteriami, oto mój zawodnik w golfa: "/.*/"(czytaj: [kod źródłowy] nie zawiera nowego wiersza)
John Dvorak

@JanDvorak Hmm, próbowałem tutaj kodu i wynik był taki sam jak kod, z wyjątkiem cytatów. Może nie tłumaczę tego dobrze, przepraszam. Muszą być wygenerowane co najmniej 2 kolory, aw jakiejś formie angielskiego zdania wynik musi generować prawdziwe słowa opisujące, jaki procent ekranu zajmuje każdy z kolorów. Może to był głupi pomysł. Myślałem, że będzie fajnie, ale może nie działać w praktyce :)
WendiKidd

Odpowiedzi:


35

Wiąz

Jeszcze nikt nie widział tej luki: demo

import Color exposing (hsl)
import Graphics.Element exposing (..)
import Mouse
import Text
import Window

msg a = centered <| Text.color a (Text.fromString "half the screen is this color")

type Pos = Upper | Lower

screen (w,h) (x,y) = 
  let (dx,dy) = (toFloat x - toFloat w / 2, toFloat h / 2 - toFloat y)
      ang = hsl (atan2 dy dx) 0.7 0.5
      ang' = hsl (atan2 dx dy) 0.7 0.5
      box c = case c of
        Upper -> container w (h // 2) middle (msg ang) |> color ang'
        Lower -> container w (h // 2) middle (msg ang') |> color ang
  in  flow down [box Upper, box Lower]

main = Signal.map2 screen Window.dimensions Mouse.position

wprowadź opis zdjęcia tutaj


3
Świetna luka!
Timtech

Uwielbiam to!!! Przynajmniej na razie dostaniesz znacznik wyboru dla sprytnych punktów. Kocham to!
WendiKidd

13
Najlepsze jest to, że nadal nie jestem pewien, które zdanie mówi o jakim kolorze.
Brilliand

3
view sourceJest umieścić tam share-elm.com i nie jest częścią skompilowanego JS / HTML.
hoosierEE

1
@ML To zależy od zakresu słowa „to”. Programiści JavaScript rozumieją ...
hoosierEE

37

JavaScript z HTML

Starałem się dokładniej odtworzyć oryginalny komiks. Zrzut ekranu jest wykonywany przy użyciu biblioteki html2canvas. Liczby są obliczane wielokrotnie, więc możesz zmienić rozmiar okna, a nawet dodać coś do strony w czasie rzeczywistym.

Wypróbuj online: http://copy.sh/xkcd-688.html

Oto zrzut ekranu:

wprowadź opis zdjęcia tutaj

<html contenteditable>
<script src=http://html2canvas.hertzen.com/build/html2canvas.js></script>
<script>
onload = function() {
    setInterval(k, 750);
    k();
}
function k() {
    html2canvas(document.body, { onrendered: t });
}
function t(c) {
    z.getContext("2d").drawImage(c, 0, 0, 300, 150);
    c = c.getContext("2d").getImageData(0, 0, c.width, c.height).data;

    for(i = y = 0; i < c.length;) 
        y += c[i++];

    y /= c.length * 255;

    x.textContent = (y * 100).toFixed(6) + "% of this website is white";

    q = g.getContext("2d");

    q.fillStyle = "#eee";
    q.beginPath();
    q.moveTo(75, 75);
    q.arc(75,75,75,0,7,false);
    q.lineTo(75,75);
    q.fill();

    q.fillStyle = "#000";
    q.beginPath();
    q.moveTo(75, 75);
    q.arc(75,75,75,0,6.28319*(1-y),false);
    q.lineTo(75,75);
    q.fill();
}
</script>
<center>
<h2 id=x></h2>
<hr>
<table><tr>
<td>Fraction of<br>this website<br>which is white _/
<td><canvas width=150 id=g></canvas>
<td>&nbsp; Fraction of<br>- this website<br>&nbsp; which is black
</table>
<hr>
0
<canvas style="border-width: 0 0 1px 1px; border-style: solid" id=z></canvas>
<h4>Location of coloured pixels in this website</h4>

Miły!! Uwielbiam podobieństwa do komiksu xkcd i fakt, że mogę zmienić tekst. Schludny! : D
WendiKidd

1
imponująca praca oO
izabera

Zręczne ... ale myślę, że musi się ustabilizować, aby być „rozwiązaniem”. Nie zastanowiłem się nad tym całkowicie - ale ponieważ niekoniecznie istnieje rozwiązanie dla arbitralnej precyzji podczas rysowania z ograniczonego zestawu cyfrowych glifów, musisz wycofać się z precyzji, jeśli nie można jej rozwiązać z wyższą precyzją próbujesz. Wyobrażam sobie, że konieczne będzie również użycie czcionki monospace, którą wstępnie obliczysz, czarno-białe piksele.
Dr. Rebmu,

Używasz 3 kolorów, więc gdzie są wartości procentowe dla szarości? ;)
ML

26

Przetwarzanie, 222 znaki

http://i.imgur.com/eQzMGzk.png

Zawsze chciałem stworzyć własną wersję tego komiksu! Najprostszym (tylko?) Sposobem, w jaki mogłem to zrobić, były próby i błędy - narysuj coś, policz, ponownie narysuj ...

Ten program ustala dokładny procent po kilku sekundach. Nie jest bardzo ładny, ale interaktywny ; możesz zmienić rozmiar okna, a ono zacznie się ponownie obliczać.

Dodano kilka nowych linii dla czytelności:

float s,S,n;
int i;
void draw(){
frame.setResizable(true);
background(255);
fill(s=i=0);
text(String.format("%.2f%% of this is white",S/++n*100),10,10);
loadPixels();
while(i<width*height)if(pixels[i++]==-1)s++;
S+=s/height/width;
}

Pokazuje tylko procent białych pikseli; Z powodu antyaliasingu tekstu nie białe piksele niekoniecznie są czarne. Im dłużej działa, tym więcej czasu będzie wymagało aktualizacji przy zmianie rozmiaru.

Edytować:

Jest to więc wyzwanie kodowe; W każdym razie grałem w golfa. Może mógłbym później dodać jakieś wykresy, ale ogólna zasada pozostanie taka sama. Myślę, że interaktywność to zgrabna część.


Bardzo dobrze!! Myślę, że dostaniesz dodatkowe uznanie za interaktywność; Bawiłem się zmieniając rozmiar okna! Bardzo fajnie :) I jesteś moją pierwszą odpowiedzią! Nie wiedziałem, czy ktoś chciałby zagrać, więc dziękuję. Zrobiłeś mój dzień. : D +1! (Jestem ciekawy, dlaczego zwalnia z upływem czasu i zbliża się do osiągnięcia prawidłowego odsetka? Jestem ciekawy, co się dzieje, nigdy wcześniej nie widziałem tego języka. wiele nowych rzeczy na temat tej strony!)
WendiKidd

headdesk Tyle że przypadkowo zapomniałem kliknąć +1. Teraz +1 ... haha. Przepraszam!
WendiKidd

1
Możesz dodać kolejną funkcję, która pozwala użytkownikom korzystać z niej za pomocą myszy, aby zwiększyć interaktywność.
AJMansfield

7
Holy box shadow, Batman
Bojangles

Jeśli chcesz grać w golfa, możesz użyć background(-1)zamiastbackground(255)
Kritixi Lithos

20

Wielkie wyzwanie. Oto moje rozwiązanie. Próbowałem zbliżyć się do oryginalnego komiksu, nawet użyłem czcionki xkcd .

Jest to aplikacja WPF, ale robiłem System.Drawingczęści do rysowania, ponieważ jestem leniwy.

Podstawowa koncepcja: w WPF okna są Visuals, co oznacza, że ​​można je renderować. Renderuję całą instancję Window na mapę bitową, liczę czarny i całkowity czarny lub biały (ignorując szarości w wygładzaniu czcionek i inne), a także liczę je za każdą trzecią obrazu (dla każdego panelu). Następnie robię to ponownie na zegarze. Osiąga równowagę w ciągu sekundy lub dwóch.

Pobieranie:

MEGA Zawsze sprawdzaj pobierane pliki pod kątem wirusów itp.

Musisz zainstalować powyższą czcionkę w swoim systemie, jeśli chcesz ją zobaczyć, w przeciwnym razie jest to domyślna czcionka WPF.

XAML:

<Window
 x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="xkcd: 688" Height="300" Width="1000" WindowStyle="ToolWindow">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.3*"/>
            <ColumnDefinition Width="0.3*"/>
            <ColumnDefinition Width="0.3*"/>
        </Grid.ColumnDefinitions>

        <Border BorderBrush="Black" x:Name="bFirstPanel" BorderThickness="3" Padding="10px" Margin="0 0 10px 0">
            <Grid>
                <Label FontSize="18" FontFamily="xkcd" VerticalAlignment="Top">Fraction of this window that is white</Label>
                <Label FontSize="18" FontFamily="xkcd" VerticalAlignment="Bottom">Fraction of this window that is black</Label>
                <Image x:Name="imgFirstPanel"></Image>
            </Grid>
        </Border>
        <Border Grid.Column="1" x:Name="bSecondPanel" BorderBrush="Black" BorderThickness="3" Padding="10px" Margin="10px 0">
            <Grid>
                <TextBlock FontSize="18" FontFamily="xkcd" VerticalAlignment="Top" HorizontalAlignment="Left">Amount of <LineBreak></LineBreak>black ink <LineBreak></LineBreak>by panel:</TextBlock>
                <Image x:Name="imgSecondPanel"></Image>
            </Grid>
        </Border>
        <Border Grid.Column="2" x:Name="bThirdPanel" BorderBrush="Black" BorderThickness="3" Padding="10px" Margin="10px 0 0 0">
            <Grid>
                <TextBlock FontSize="18" FontFamily="xkcd" VerticalAlignment="Top" HorizontalAlignment="Left">Location of <LineBreak></LineBreak>black ink <LineBreak></LineBreak>in this window:</TextBlock>
                <Image x:Name="imgThirdPanel"></Image>
            </Grid>
        </Border>

    </Grid>
</Window>

Kod:

using System;
using System.Drawing;
using System.Timers;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Brushes = System.Drawing.Brushes;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        private Timer mainTimer = new Timer();
        public MainWindow()
        {
            InitializeComponent();

            Loaded += (o1,e1) =>
                          {
                              mainTimer = new Timer(1000/10);
                              mainTimer.Elapsed += (o, e) => {
                                  try
                                  {
                                      Dispatcher.Invoke(Refresh);
                                  } catch(Exception ex)
                                  {
                                      // Nope
                                  }
                              };
                              mainTimer.Start();
                          };
        }

        private void Refresh()
        {
            var actualh = this.RenderSize.Height;
            var actualw = this.RenderSize.Width;

            var renderTarget = new RenderTargetBitmap((int) actualw, (int) actualh, 96, 96, PixelFormats.Pbgra32);
            var sourceBrush = new VisualBrush(this);

            var visual = new DrawingVisual();
            var context = visual.RenderOpen();

            // Render the window onto the target bitmap
            using (context)
            {
                context.DrawRectangle(sourceBrush, null, new Rect(0,0, actualw, actualh));
            }
            renderTarget.Render(visual);

            // Create an array with all of the pixel data
            var stride = (int) actualw*4;
            var data = new byte[stride * (int)actualh];
            renderTarget.CopyPixels(data, stride, 0);

            var blackness = 0f;
            var total = 0f;

            var blacknessFirstPanel = 0f;
            var blacknessSecondPanel = 0f;
            var blacknessThirdPanel = 0f;
            var totalFirstPanel = 0f;
            var totalSecondPanel = 0f;
            var totalThirdPanel = 0f;

            // Count all of the things
            for (var i = 0; i < data.Length; i += 4)
            {
                var b = data[i];
                var g = data[i + 1];
                var r = data[i + 2];

                if (r == 0 && r == g && g == b)
                {
                    blackness += 1;
                    total += 1;

                    var x = i%(actualw*4) / 4;

                    if(x < actualw / 3f)
                    {
                        blacknessFirstPanel += 1;
                        totalFirstPanel += 1;
                    } else if (x < actualw * (2f / 3f))
                    {
                        blacknessSecondPanel += 1;
                        totalSecondPanel += 1;
                    }
                    else if (x < actualw)
                    {
                        blacknessThirdPanel += 1;
                        totalThirdPanel += 1;
                    }
                } else if (r == 255 && r == g && g == b)
                {
                    total += 1;

                    var x = i % (actualw * 4) / 4;

                    if (x < actualw / 3f)
                    {
                        totalFirstPanel += 1;
                    }
                    else if (x < actualw * (2f / 3f))
                    {
                        totalSecondPanel += 1;
                    }
                    else if (x < actualw)
                    {
                        totalThirdPanel += 1;
                    }
                }
            }

            var black = blackness/total;

            Redraw(black, blacknessFirstPanel, blacknessSecondPanel, blacknessThirdPanel, blackness, renderTarget);
        }

        private void Redraw(double black, double firstpanel, double secondpanel, double thirdpanel, double totalpanels, ImageSource window)
        {
            DrawPieChart(black);
            DrawBarChart(firstpanel, secondpanel, thirdpanel, totalpanels);
            DrawImage(window);
        }

        void DrawPieChart(double black)
        {
            var w = (float)bFirstPanel.ActualWidth;
            var h = (float)bFirstPanel.ActualHeight;
            var padding = 0.1f;

            var b = new Bitmap((int)w, (int)h);
            var g = Graphics.FromImage(b);

            var px = padding*w;
            var py = padding*h;

            var pw = w - (2*px);
            var ph = h - (2*py);

            g.DrawEllipse(Pens.Black, px,py,pw,ph);

            g.FillPie(Brushes.Black, px, py, pw, ph, 120, (float)black * 360);

            g.DrawLine(Pens.Black, 30f, h * 0.1f, w / 2 + w * 0.1f, h / 2 - h * 0.1f);
            g.DrawLine(Pens.Black, 30f, h - h * 0.1f, w / 2 - w * 0.2f, h / 2 + h * 0.2f);

            imgFirstPanel.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(b.Width, b.Height));
        }

        void DrawBarChart(double b1, double b2, double b3, double btotal)
        {
            var w = (float)bFirstPanel.ActualWidth;
            var h = (float)bFirstPanel.ActualHeight;
            var padding = 0.1f;

            var b = new Bitmap((int)w, (int)h);
            var g = Graphics.FromImage(b);

            var px = padding * w;
            var py = padding * h;

            var pw = w - (2 * px);
            var ph = h - (2 * py);

            g.DrawLine(Pens.Black, px, py, px, ph+py);
            g.DrawLine(Pens.Black, px, py + ph, px+pw, py+ph);

            var fdrawbar = new Action<int, double>((number, value) =>
                {
                    var height = ph*(float) value/(float) btotal;
                    var width = pw/3f - 4f;

                    var x = px + (pw/3f)*(number-1);
                    var y = py + (ph - height);

                    g.FillRectangle(Brushes.Black, x, y, width, height);
                });

            fdrawbar(1, b1);
            fdrawbar(2, b2);
            fdrawbar(3, b3);

            imgSecondPanel.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(b.Width, b.Height));
        }

        void DrawImage(ImageSource window)
        {
            imgThirdPanel.Source = window;
        }
    }
}

Kod nie został wyczyszczony, ale przepraszam, powinien być nieco czytelny.


2
Późne wejście, ale jedno z najlepszych.
primo

14

C (z SDL i SDL_ttf): Rozwiązanie w skali szarości

Oto rozwiązanie, które wykorzystuje formę wykresu kołowego do przechwytywania pełnego spektrum kolorów pikseli w skali szarości, z taktowaniem poniżej 100 linii.

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "SDL.h"
#include "SDL_ttf.h"

int main(void)
{
    SDL_Surface *screen, *buffer, *caption;
    SDL_Color pal[256];
    SDL_Rect rect;
    SDL_Event event;
    TTF_Font *font;
    int levels[256], plev[256];
    Uint8 *p;
    float g;
    int cr, redraw, hoffset, h, n, v, w, x, y;

    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();
    screen = SDL_SetVideoMode(640, 480, 0, SDL_ANYFORMAT | SDL_RESIZABLE);
    font = TTF_OpenFont(FONTPATH, 24);
    buffer = 0;
    for (;;) {
        if (!buffer) {
            buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h,
                                          8, 0, 0, 0, 0);
            for (n = 0 ; n < 256 ; ++n)
                pal[n].r = pal[n].g = pal[n].b = n;
            SDL_SetColors(buffer, pal, 0, 256);
        }
        memcpy(plev, levels, sizeof levels);
        memset(levels, 0, sizeof levels);
        SDL_LockSurface(buffer);
        p = buffer->pixels;
        for (h = 0 ; h < buffer->h ; ++h) {
            for (w = 0 ; w < buffer->w ; ++w)
                ++levels[p[w]];
            p += buffer->pitch;
        }
        for (n = 1 ; n < 256 ; ++n)
            levels[n] += levels[n - 1];
        redraw = memcmp(levels, plev, sizeof levels);
        if (redraw) {
            SDL_UnlockSurface(buffer);
            SDL_FillRect(buffer, NULL, 255);
            caption = TTF_RenderText_Shaded(font,
                        "Distribution of pixel color in this image",
                        pal[0], pal[255]);
            rect.x = (buffer->w - caption->w) / 2;
            rect.y = 4;
            hoffset = caption->h + 4;
            SDL_BlitSurface(caption, NULL, buffer, &rect);
            SDL_FreeSurface(caption);
            SDL_LockSurface(buffer);
            cr = buffer->h - hoffset;
            cr = (cr < buffer->w ? cr : buffer->w) / 2 - 4;
            p = buffer->pixels;
            for (h = 0 ; h < buffer->h ; ++h) {
                y = h - (screen->h + hoffset) / 2;
                for (w = 0 ; w < buffer->w ; ++w) {
                    x = w - buffer->w / 2;
                    g = sqrtf(x * x + y * y);
                    if (g < cr - 1) {
                        g = atanf((float)y / (x + g));
                        v = levels[255] * (g / M_PI + 0.5);
                        for (n = 0 ; n < 255 && levels[n] < v ; ++n) ;
                        p[w] = n;
                    } else if (g < cr + 1) {
                        p[w] = (int)(128.0 * fabs(g - cr));
                    }
                }
                p += buffer->pitch;
            }
        }
        SDL_UnlockSurface(buffer);
        SDL_BlitSurface(buffer, NULL, screen, NULL);
        SDL_UpdateRect(screen, 0, 0, 0, 0);
        if (redraw ? SDL_PollEvent(&event) : SDL_WaitEvent(&event)) {
            if (event.type == SDL_QUIT)
                break;
            if (event.type == SDL_VIDEORESIZE) {
                SDL_SetVideoMode(event.resize.w, event.resize.h, 0,
                                 SDL_ANYFORMAT | SDL_RESIZABLE);
                SDL_FreeSurface(buffer);
                buffer = 0;
            }
        }
    }
    SDL_Quit();
    TTF_Quit();
    return 0;
}

Podobnie jak w moim poprzednim rozwiązaniu, ścieżka do pliku czcionek musi być albo zakodowana na stałe w źródle, albo dodana do komendy build, np .:

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH=`fc-match --format='"%{file}"' :bold`
    xkcdgolf.c -lSDL_ttf `sdl-config --libs` -lm

Dane wyjściowe programu wyglądają następująco:

Wykres kołowy pokazujący pełny rozkład kolorów w skali szarości

Ten jest fajny do oglądania, ponieważ cała matematyka spowalnia przerysowywanie do miejsca, w którym można zobaczyć program zerujący na stabilnym rozwiązaniu. Pierwsze oszacowanie jest szalone (ponieważ powierzchnia zaczyna się całkowicie czarna), a następnie zmniejsza się do ostatecznego rozmiaru po około kilkunastu iteracjach.

Kod działa na podstawie liczby ludności każdego koloru piksela na bieżącym obrazie. Jeśli ta liczba ludności nie pasuje do ostatniej, przerysowuje obraz. Kod iteruje się po każdym pikselu, ale przekształca współrzędne x, y we współrzędne biegunowe, obliczając najpierw promień (wykorzystując środek obrazu jako początek). Jeśli promień mieści się w obszarze wykresu kołowego, oblicza theta. Theta można łatwo skalować do liczby ludności, która określa kolor piksela. Z drugiej strony, jeśli promień jest tuż przy granicy wykresu kołowego, wówczas obliczana jest wartość wygładzania, aby narysować okrąg wokół zewnętrznej części wykresu. Współrzędne biegunowe ułatwiają wszystko!


Najczęściej używasz floatwersji funkcji biblioteki matematycznej, ale czy nie powinno fabsbyć fabsf?
luser droog

Technicznie może, ale fabs()jest bardziej przenośny.
breadbox

To prawda, że ​​miałem problem z tym, że nie zdefiniowano go w nagłówkach, nawet jeśli jest obecny w bibliotece. Ponadto osiąga się mniej wydajności niż w przypadku transcendentałów. :)
luser droog

10

C (z SDL i SDL_ttf)

Oto bardzo prosta implementacja, w około 60 liniach kodu C:

#include <stdio.h>
#include "SDL.h"
#include "SDL_ttf.h"

int main(void)
{
    char buf[64];
    SDL_Surface *screen, *text;
    SDL_Rect rect;
    SDL_Color black;
    SDL_Event event;
    TTF_Font *font;
    Uint32 blackval, *p;
    int size, b, prevb, h, i;

    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();
    screen = SDL_SetVideoMode(640, 480, 32, SDL_ANYFORMAT | SDL_RESIZABLE);
    font = TTF_OpenFont(FONTPATH, 32);
    black.r = black.g = black.b = 0;
    blackval = SDL_MapRGB(screen->format, 0, 0, 0);

    b = -1;
    for (;;) {
        prevb = b;
        b = 0;
        SDL_LockSurface(screen);
        p = screen->pixels;
        for (h = screen->h ; h ; --h) {
            for (i = 0 ; i < screen->w ; ++i)
                b += p[i] == blackval;
            p = (Uint32*)((Uint8*)p + screen->pitch);
        }
        SDL_UnlockSurface(screen);
        size = screen->w * screen->h;
        SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
        sprintf(buf, "This image is %.2f%% black pixels", (100.0 * b) / size);
        text = TTF_RenderText_Solid(font, buf, black);
        rect.x = (screen->w - text->w) / 2;
        rect.y = screen->h / 2 - text->h;
        SDL_BlitSurface(text, NULL, screen, &rect);
        SDL_FreeSurface(text);
        sprintf(buf, "and %.2f%% white pixels.", (100.0 * (size - b)) / size);
        text = TTF_RenderText_Solid(font, buf, black);
        rect.x = (screen->w - text->w) / 2;
        rect.y = screen->h / 2;
        SDL_BlitSurface(text, NULL, screen, &rect);
        SDL_FreeSurface(text);
        SDL_UpdateRect(screen, 0, 0, 0, 0);
        if (b == prevb ? SDL_WaitEvent(&event) : SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT)
                break;
            if (event.type == SDL_VIDEORESIZE)
                SDL_SetVideoMode(event.resize.w, event.resize.h, 32,
                                 SDL_ANYFORMAT | SDL_RESIZABLE);
        }
    }

    TTF_Quit();
    SDL_Quit();
    return 0;
}

Aby to skompilować, musisz zdefiniować FONTPATHwskazywanie pliku .ttf czcionki, która ma być używana:

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH='"/usr/share/fonts/truetype/freefont/FreeSansBold.ttf"'
    xkcdgolf.c -lSDL_ttf `sdl-config --libs`

Na większości nowoczesnych maszyn z systemem Linux można użyć fc-matchnarzędzia do wyszukiwania lokalizacji czcionek, dzięki czemu polecenie kompilacji staje się:

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH=`fc-match --format='"%{file}"' :bold`
    xkcdgolf.c -lSDL_ttf `sdl-config --libs`

(Oczywiście możesz zastąpić żądaną czcionkę ulubioną czcionką).

Kod w szczególności nie żąda wygładzania, dlatego okno zawiera tylko czarno-białe piksele.

Wreszcie zainspirowało mnie eleganckie rozwiązanie @ daniero, umożliwiające zmianę rozmiaru okna. Przekonasz się, że czasami program oscyluje między zliczeniami, utknął na orbicie wokół atraktora, do którego nigdy nie może dotrzeć. Kiedy tak się stanie, po prostu zmień rozmiar okna, aż się zatrzyma.

I, na żądanie, oto jak to wygląda po uruchomieniu go w moim systemie:

Ten obraz to 3,36% czarnych pikseli i 96,64% białych pikseli.

Wreszcie uważam, że powinienem wskazać, na wypadek, gdyby ktokolwiek tutaj jeszcze tego nie widział, że MAA opublikowała wywiad z Randallem Munroe, w którym szczegółowo omawia tworzenie kreskówki nr 688.


1
Bardzo fajne rozwiązanie. Czy mógłbyś zamieścić kilka zrzutów ekranu z uruchomionego programu, w ślad za postem @ daniero? :)
Alex Brooks,

+1, bardzo miło! Dzięki za dodanie zrzutu ekranu :) Link do wywiadu jest interesujący, dzięki!
WendiKidd

9

wprowadź opis zdjęcia tutaj

Obraz ma wymiary 100 x 100, a liczby są dokładne i mam na myśli dokładne - wybrałem obraz 10000 pikseli, aby odsetki mogły być wyrażone z dokładnością do dwóch miejsc po przecinku. Metoda polegała na odrobinie matematyki, zgadywaniu i pewnej liczbie zgryzień w Pythonie.

Widząc, jak wcześniej wiedziałem, że wartości procentowe mogą być wyrażone w 4 cyfrach, policzyłem, ile czarnych pikseli było w każdej z cyfr od 0 do 9, w 8-pikselowej wysokości Arial, w której jest napisany tekst. Napisałem szybka funkcja, weightktóra informuje, ile pikseli potrzeba do napisania danej liczby, po lewej stronie uzupełnione zerami, aby miały 4 cyfry:

def weight(x):
    total = 4 * px[0]
    while x > 0:
       total = total - px[0] + px[x % 10]
       x = x / 10
    return total

pxto tablica odwzorowująca cyfry na liczbę wymaganych pikseli. Jeśli B to liczba czarnych pikseli, a W to liczba białych pikseli, mamy B + W = 10000i potrzebujemy:

B = 423 + weight(B) + weight(W)
W = 9577 - weight(B) - weight(W)

Skąd pochodzą stałe? 423 to „początkowa” liczba czarnych pikseli, liczba czarnych pikseli w tekście bez liczb. 9577 to liczba początkowych białych pikseli. Musiałem dostosować ilość początkowych czarnych pikseli kilka razy, zanim udało mi się uzyskać stałe, tak że powyższy system ma nawet rozwiązanie. Dokonano tego zgadując i krzyżując palce.

Powyższy system jest okropnie nieliniowy, więc oczywiście możesz zapomnieć o rozwiązywaniu go symbolicznie, ale możesz po prostu przejrzeć każdą wartość B, ustawić W = 10000 - B i wyraźnie sprawdzić równania.

>>> for b in range(10000 + 1):
...     if b == weight(b) + weight(10000 - b)+423: print b;
...
562
564

Może zrób zdjęcie o wymiarach 250 x 400, aby dostać go do 3 miejsc po przecinku i wyświetlić w międzyczasie więcej tekstu.
Joe Z.

Bardzo fajne rozwiązanie, matematyka o brutalnej sile zawsze może rozwiązać tego rodzaju problemy!
KPCh

6

QBasic

Ponieważ nostalgia.

A ponieważ tak naprawdę nie wiem, żadne biblioteki obrazów to współczesne języki.

SCREEN 9

CONST screenWidth = 640
CONST screenHeight = 350
CONST totalPixels# = screenWidth * screenHeight

accuracy = 6

newWhite# = 0
newGreen# = 0
newBlack# = totalPixels#

DO
    CLS
    white# = newWhite#
    green# = newGreen#
    black# = newBlack#

    ' Change the precision of the percentages every once in a while
    ' This helps in finding values that converge
    IF RND < .1 THEN accuracy = INT(RND * 4) + 2
    format$ = "###." + LEFT$("######", accuracy) + "%"

    ' Display text
    LOCATE 1
    PRINT "Percentage of the screen which is white:";
    PRINT USING format$; pct(white#)
    LOCATE 4
    PRINT white#; "/"; totalPixels#; "pixels"
    LOCATE 7
    PRINT "Percentage of the screen which is black:";
    PRINT USING format$; pct(black#)
    LOCATE 10
    PRINT black#; "/"; totalPixels#; "pixels"
    LOCATE 13
    PRINT "Percentage of the screen which is green:";
    PRINT USING format$; pct(green#)
    LOCATE 16
    PRINT green#; "/"; totalPixels#; "pixels"

    ' Display bar graphs
    LINE (0, 16)-(pct(white#) / 100 * screenWidth, 36), 2, BF
    LINE (0, 100)-(pct(black#) / 100 * screenWidth, 120), 2, BF
    LINE (0, 184)-(pct(green#) / 100 * screenWidth, 204), 2, BF

    newBlack# = pixels#(0)
    newGreen# = pixels#(2)
    newWhite# = pixels#(15)
LOOP UNTIL black# = newBlack# AND white# = newWhite# AND green# = newGreen#

' Wait for user keypress before ending program: otherwise the "Press any
' key to continue" message would instantly make the results incorrect!
x$ = INPUT$(1)


FUNCTION pixels# (colr)
' Counts how many pixels of the given color are on the screen

pixels# = 0

FOR i = 0 TO screenWidth - 1
    FOR j = 0 TO screenHeight - 1
        IF POINT(i, j) = colr THEN pixels# = pixels# + 1
    NEXT j
NEXT i

END FUNCTION

FUNCTION pct (numPixels#)
' Returns percentage, given a number of pixels

pct = numPixels# / totalPixels# * 100

END FUNCTION

Dość prosta metoda liczenia i powtarzania wyników. Główną „interesującą” rzeczą jest to, że program losowo wypróbowuje różne precyzyjne wartości procentowe - stwierdziłem, że nie zawsze było inaczej.

I dane wyjściowe (testowane na QB64 ):

QBasic metagraph


3

AWK

... z netpbm i innymi pomocnikami

Plik „x”:

BEGIN {
        FS=""
        n++
        while(n!=m) {
                c="printf '%s\n' '"m"% black pixels'"
                c=c" '"100-m"% white pixels'"
                c=c" | pbmtext -space 1 -lspace 1 | pnmtoplainpnm | tee x.pbm"
                n=m
                delete P
                nr=0
                while(c|getline==1) if(++nr>2) for(i=1;i<=NF;i++) P[$i]++
                close(c)
                m=100*P[1]/(P[0]+P[1])
                print m"%"
        }
}

Bieg:

$ awk -f x
4.44242%
5.2424%
5.04953%
5.42649%
5.27746%
5.1635%
5.15473%
5.20733%
5.20733%

Obraz jest zapisany jako „x.pbm”, przekonwertowałem go do formatu png do przesłania:

x.png

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.