Défi xkcd: "Pourcentage de l'écran en couleur [x]"


57

Donc, je pense que nous avons tous probablement déjà vu cette bande dessinée xkcd :

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

Cela peut être trop général ou trop difficile, je ne suis pas sûr. Mais le défi est de créer un programme dans n’importe quelle langue qui crée une fenêtre d’au moins 2 couleurs et affichant en mots anglais quel pourcentage de l’écran correspond à chaque couleur.

ex. La solution la plus simple serait un fond blanc avec des lettres noires indiquant "Pourcentage de cette image qui est noir: [x]%. Pourcentage de cette image qui est blanc: [y]%"

Vous pouvez devenir aussi fou ou aussi simple que vous le souhaitez. Le texte brut est une solution valable, mais si vous créez des images intéressantes comme dans la bande dessinée xkcd, c'est encore mieux! Le gagnant sera la solution la plus amusante et créative qui obtiendra le plus de votes. Alors allez-y et faites quelque chose d'amusant et digne de xkcd! :)

Alors qu'est-ce que tu en penses? Cela ressemble à un défi amusant? :)

S'il vous plaît inclure une capture d'écran de votre programme en cours d'exécution dans votre réponse :)


6
Un programme "ce programme a 64 A, 4 B, ... et 34 guillemets dans le code source" serait plus intéressant :-)
John Dvorak

2
OK ... quels sont les critères objectifs gagnants? Comment déterminez-vous si une sortie spécifique est valide? Suffit-il que cela soit vrai et décrive une propriété de lui-même numériquement?
John Dvorak

@ JanDvorak Oh, c'est un bon! Le programme alphabet est en fait ce qui m'a fait penser à cela à l'origine, mais je n'ai pas envisagé d'y ajouter l'élément de code source! Vous devriez poster cela comme une question :) Oui, il suffit que ce soit vrai et se décrive. Hmm, vous avez raison cependant, je n'ai pas pensé à la façon dont je prouverais que les résultats finaux étaient corrects. Je suppose qu'il me faut un moyen de compter tous les pixels de chaque couleur dans une image de résultat. Je vais aller enquêter là-dessus maintenant. (Désolé, ma première question a eu des problèmes ... J'ai essayé mais je suis nouveau à cela! Merci :))
WendiKidd

si la véracité et l'auto-référence sont des critères suffisants, voici mon candidat au logiciel de graphisme: "/.*/"(lire: [le code source] ne contient pas de nouvelle ligne)
John Dvorak

@JanDvorak Hmm, j'ai essayé votre code ici et la sortie était la même que le code, sauf sans les guillemets. Peut-être que je n'explique pas cela correctement, désolé. Au moins 2 couleurs doivent être générées et, sous une forme de phrase anglaise, la sortie doit générer des mots exacts décrivant le pourcentage d'écran occupé par chacune des couleurs. C'était peut-être une idée idiote. Je pensais que ce serait amusant mais que cela ne fonctionnerait pas dans la pratique :)
WendiKidd

Réponses:


35

Orme

Je n'ai encore vu personne utiliser cette échappatoire: démo

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

entrez la description de l'image ici


3
Grande échappatoire!
Timtech

J'aime cela!!! Au moins pour l'instant, vous obtenez la coche pour les points astucieux. Aimer!
WendiKidd

13
La meilleure partie est que je ne sais toujours pas quelle phrase parle de quelle couleur.
Brilliand

3
Le view sourceest mis là par share-elm.com et ne fait pas partie du JS / HTML compilé.
hoosierEE

1
@ML Cela dépend de la portée du mot "ceci". Les programmeurs JavaScript comprennent ...
hoosierEE

37

JavaScript avec HTML

J'ai essayé de reproduire la bande dessinée originale plus précisément. Une capture d'écran est réalisée à l'aide de la bibliothèque html2canvas. Les nombres étant calculés à plusieurs reprises, vous pouvez redimensionner la fenêtre ou même ajouter quelque chose à la page en temps réel.

Essayez-le en ligne: http://copy.sh/xkcd-688.html

Voici une capture d'écran:

entrez la description de l'image ici

<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>

Agréable!! J'adore les similitudes avec la bande dessinée xkcd et le fait que je puisse changer le texte. Soigné! : D
WendiKidd

1
travail impressionnant oO
izabera

Nifty ... mais je pense que ça doit se stabiliser pour être une "solution". Vous n'avez pas encore réfléchi à tout cela, mais comme il n'existe pas forcément de solution de précision arbitraire pour dessiner à partir d'un ensemble limité de glyphes à chiffres, vous devrez annuler la précision si elle ne peut pas être résolue avec une précision supérieure. vous essayez. J'imagine que l'utilisation d'une police monospace permettant de pré-calculer les pixels noir / blanc sera également nécessaire.
Dr. Rebmu

Vous utilisez 3 couleurs, alors où sont les pourcentages pour le gris? ;)
ML

26

Traitement, 222 caractères

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

J'ai toujours voulu créer ma propre version de cette bande dessinée! La façon la plus simple (la seule?) Que je puisse faire était essais et erreurs - dessiner quelque chose, compter, compter encore ...

Ce programme établit un pourcentage précis après quelques secondes. Ce n'est pas très joli, mais c'est interactif ; vous pouvez redimensionner la fenêtre et celle-ci commencera à se recalculer.

Ajout de nouvelles lignes pour la lisibilité:

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

Il ne montre que le pourcentage de pixels blancs; En raison de l'anticrénelage du texte, les pixels non blancs ne sont pas nécessairement noirs. Plus il fonctionne longtemps, plus il aura besoin de temps pour se mettre à jour lors d'un redimensionnement.

Modifier:

Donc, c'est un défi de code; Je l'ai en quelque sorte joué au golf. Je pourrais peut-être ajouter une sorte de graphique plus tard, mais le principe général resterait le même. L'interactivité est la partie la plus intéressante, je pense.


Très agréable!! Je pense que vous obtenez un crédit supplémentaire pour l'interactivité; Je me suis amusé à redimensionner la fenêtre! Très cool :) Et vous êtes ma première réponse! Je ne savais pas si quelqu'un voudrait jouer, alors merci. Tu as fait ma journée. : D +1! (Je suis curieux cependant, pourquoi cela ralentit-il avec le temps et que nous approchons du bon pourcentage? Je suis curieux de savoir ce qui se passe, je n'avais jamais vu cette langue auparavant. Beaucoup de nouvelles choses à fouiller sur ce site!)
WendiKidd

headdesk Sauf que j'ai accidentellement oublié de cliquer sur le +1. Maintenant +1 ... haha. Pardon!
WendiKidd

1
Vous pouvez ajouter une autre fonction permettant aux utilisateurs d’y dessiner avec la souris, pour une interactivité accrue.
AJMansfield

7
Ombre sainte boîte, Batman
Bojangles

Si vous voulez background(-1)background(255)
jouer

20

Grand défi. Voici ma solution. J'ai essayé de me rapprocher le plus possible de la bande dessinée originale, j'ai même utilisé la police xkcd .

C'est une application WPF, mais j'avais l'habitude System.Drawingde dessiner parce que je suis paresseux.

Concept de base: Dans WPF, les fenêtres sont Visuals, ce qui signifie qu'elles peuvent être rendues. Je convertis toute l'occurrence de Window en bitmap, compte le noir et le total en noir et blanc (en ignorant les gris dans le lissage des polices), ainsi que chaque tiers de l'image (pour chaque panneau). Ensuite, je le fais à nouveau sur une minuterie. Il atteint l'équilibre en une seconde ou deux.

Télécharger:

MEGA Vérifiez toujours que les fichiers que vous téléchargez ne contiennent pas de virus, etc.

Si vous souhaitez la voir, vous devez installer la police ci-dessus sur votre système. Sinon, il s'agit de la police WPF par défaut.

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>

Code:

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

Le code n'est pas nettoyé, mais il devrait être assez lisible, désolé.


2
Une entrée tardive, mais l'un des meilleurs.
dimanche

14

C (avec SDL et SDL_ttf): solution en niveaux de gris

Voici une solution qui tire parti du diagramme à secteurs pour capturer l’éventail complet des couleurs de pixels en niveaux de gris, avec un peu moins de 100 lignes.

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

Comme avec ma solution précédente, le chemin du fichier de police doit être codé en dur dans le source ou ajouté à la commande de construction, par exemple:

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

La sortie du programme ressemble à ceci:

Graphique à secteurs montrant la distribution complète des couleurs en pixels en niveaux de gris

Celui-ci est amusant à regarder, parce que toutes les maths ralentissent les redessins où vous pouvez voir le programme se focaliser sur la solution stable. La première estimation est complètement décalée (puisque la surface est entièrement noire), puis redescend à la taille finale après environ une douzaine d'itérations.

Le code fonctionne en prenant le nombre d'habitants de chaque couleur de pixel dans l'image en cours. Si cette population ne correspond pas à la dernière, elle redessine l'image. Le code itère sur chaque pixel, mais il transforme les coordonnées x, y en coordonnées polaires, en calculant d'abord le rayon (en utilisant le centre de l'image comme origine). Si le rayon se trouve dans la zone du graphique à secteurs, il calcule le thêta. Le thêta s’adapte facilement au nombre d’habitants, ce qui détermine la couleur des pixels. Par contre, si le rayon se trouve juste à la frontière du graphique à secteurs, une valeur anti-aliasée est calculée pour tracer le cercle autour de l'extérieur du graphique. Les coordonnées polaires rendent tout facile!


Vous utilisez principalement les floatversions des fonctions de la bibliothèque mathématique, mais cela ne devrait pas fabsêtre le cas fabsf?
luser droog

Techniquement, peut-être, mais fabs()est plus portable.
breadbox

Certes, j'ai eu du mal à ce que celui-ci ne soit pas défini dans les en-têtes même lorsqu'il est présent dans la bibliothèque. En outre, il y a moins de performance à gagner qu'avec les transcendantaux. :)
luser droog

10

C (avec SDL et SDL_ttf)

Voici une implémentation très simple, en environ 60 lignes de code 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;
}

Pour compiler cela, vous devez définir FONTPATHde pointer sur un fichier .ttf de la police à utiliser:

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

Sur la plupart des machines Linux modernes, vous pouvez utiliser cet fc-matchutilitaire pour rechercher les emplacements des polices. La commande de compilation devient:

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

(Bien sûr, vous pouvez remplacer la police demandée par votre favori personnel.)

Le code ne demande spécifiquement aucun anti-aliasing, de sorte que la fenêtre ne contienne que des pixels en noir et blanc.

Enfin, @ daniero m'a inspiré la solution élégante permettant le redimensionnement de la fenêtre. Vous verrez que parfois le programme oscille entre les comptes, bloqué dans une orbite autour d'un attracteur qu'il ne peut jamais atteindre. Lorsque cela se produit, redimensionnez un peu la fenêtre jusqu'à ce qu'elle s'arrête.

Et, à la demande, voici à quoi cela ressemble quand je l’exécute sur mon système:

Cette image est 3,36% de pixels noirs et 96,64% de pixels blancs.

Enfin, j’estime que je devrais signaler, au cas où personne ici ne l’aurait déjà vu, que le MAA avait publié une interview de Randall Munroe dans laquelle il discutait en détail de la fabrication du dessin animé n ° 688.


1
Très belle solution. Pourriez-vous éventuellement insérer quelques captures d'écran du programme en cours, à la suite du message de @ daniero? :)
Alex Brooks

+1, très sympa! Merci d'avoir ajouté la capture d'écran :) Et le lien vers l'interview est intéressant, merci!
WendiKidd

9

entrez la description de l'image ici

L'image est 100x100 et les chiffres sont exacts, et je veux dire exactement - j'ai choisi une image de 10000 pixels pour que les pourcentages puissent être exprimés avec deux décimales. La méthode consistait en un peu de maths, de devinettes et de calculs en Python.

Comme je savais d'avance que les pourcentages pouvaient être exprimés en 4 chiffres, j'ai compté le nombre de pixels noirs dans chacun des chiffres de 0 à 9, en Arial de 8 pixels de haut, dans lequel le texte est écrit. fonction rapide weightqui vous indique le nombre de pixels nécessaires pour écrire un nombre donné, complétée à gauche par des zéros de 4 chiffres:

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

pxest un tableau mappant les chiffres sur le nombre de pixels requis. Si B est le nombre de pixels noirs et W le nombre de pixels blancs, nous avons B + W = 10000et nous avons besoin de:

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

D'où viennent les constantes? 423 est le nombre "initial" de pixels noirs, le nombre de pixels noirs dans le texte sans les nombres. 9577 est le nombre de pixels blancs initiaux. J'ai dû ajuster la quantité de pixels noirs initiaux plusieurs fois avant de pouvoir obtenir des constantes telles que le système ci-dessus ait même une solution. Cela a été fait en devinant et en me croisant les doigts.

Le système ci-dessus est horriblement non linéaire, vous pouvez donc évidemment oublier de le résoudre symboliquement, mais vous pouvez simplement parcourir chaque valeur de B, définir W = 10000 - B et vérifier les équations de manière explicite.

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

Faites peut-être une image 250 x 400 pour pouvoir l’obtenir avec 3 décimales et afficher plus de texte entre-temps.
Joe Z.

Très bonne solution, certains maths en force brute peuvent toujours résoudre ce genre de problèmes!
CCP

6

QBasic

Parce que la nostalgie

Et parce que je ne connais pas vraiment aucune bibliothèque d’images, c’est le langage moderne.

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

Méthode assez simple de sortie-comptage-répétition. La principale chose "intéressante" est que le programme essaie au hasard différentes précisions pour les pourcentages - j'ai constaté qu'il ne convergeait pas toujours autrement.

Et la sortie (testée sur QB64 ):

Métagraphe QBasic


3

AWK

... avec netpbm et d'autres aides

Le fichier '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"%"
        }
}

La course:

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

L'image est écrite en tant que 'x.pbm', je l'ai convertie en png pour le téléchargement:

x.png

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.