xkcd challenge: “Persentase layar yang berwarna [x]”


57

Jadi saya pikir kita semua mungkin pernah melihat komik xkcd ini :

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

Ini mungkin terlalu umum atau terlalu sulit, saya tidak yakin. Tetapi tantangannya adalah membuat program dalam bahasa apa pun yang menciptakan jendela yang memiliki setidaknya 2 warna dan menampilkan dalam kata-kata bahasa Inggris berapa persentase layar masing-masing warna.

ex. Solusi paling sederhana adalah latar belakang putih dengan huruf hitam bertuliskan "Persentase gambar ini berwarna hitam: [x]%. Persentase gambar ini berwarna putih: [y]%"

Anda bisa menjadi gila atau sesederhana yang Anda inginkan; teks biasa adalah solusi yang valid tetapi jika Anda membuat gambar menarik seperti di komik xkcd itu lebih baik! Pemenang akan menjadi solusi paling menyenangkan dan kreatif yang mendapat suara terbanyak. Jadi maju dan buat sesuatu yang menyenangkan dan layak untuk xkcd! :)

Jadi apa yang Anda pikirkan? Kedengarannya seperti tantangan yang menyenangkan? :)

Harap sertakan tangkapan layar program Anda yang berjalan dalam jawaban Anda :)


6
Program "program ini memiliki program 64 A, 4 B, ... dan 34 tanda kutip ganda dalam kode sumber" akan lebih menarik :-)
John Dvorak

2
OKE ... apa kriteria menang objektif? Bagaimana Anda menentukan apakah ada output spesifik yang valid? Apakah cukup bahwa itu benar dan menggambarkan properti itu sendiri secara numerik?
John Dvorak

@ JanDvorak Oh, itu bagus! Program alfabet sebenarnya yang membuat saya berpikir tentang ini pada awalnya, tapi saya tidak mempertimbangkan untuk menambahkan elemen kode sumber ke dalamnya! Anda harus memposting itu sebagai pertanyaan :) Ya, sudah cukup bahwa itu benar dan menggambarkan dirinya sendiri. Hmm, kamu benar juga, aku tidak memikirkan bagaimana aku akan membuktikan bahwa hasil akhirnya benar. Saya akan membutuhkan cara untuk menghitung semua piksel dari setiap warna dalam gambar hasil, saya kira. Saya akan menyelidiki itu sekarang. (Maaf pertanyaan pertama saya punya masalah ... Saya mencoba tapi saya baru dalam hal ini! Terima kasih :))
WendiKidd

jika kebenaran dan referensi-diri adalah kriteria yang memadai, inilah kontestan skrip golf saya: "/.*/"(baca: [kode sumber] tidak mengandung baris baru)
John Dvorak

@JanDvorak Hmm, saya mencoba kode Anda di sini dan hasilnya sama dengan kode kecuali tanpa tanda kutip. Mungkin saya tidak menjelaskan ini dengan benar, maaf. Paling tidak harus ada 2 warna yang dihasilkan, dan dalam beberapa bentuk kalimat bahasa Inggris output harus menghasilkan kata-kata benar yang menggambarkan berapa persentase layar yang ditempati masing-masing warna. Mungkin ini ide yang konyol. Saya pikir ini akan menyenangkan tetapi mungkin tidak akan berhasil dalam praktik :)
WendiKidd

Jawaban:


35

Elm

Belum melihat orang menggunakan celah ini: 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

masukkan deskripsi gambar di sini


3
Celah yang bagus!
Timtech

Aku suka ini!!! Setidaknya untuk saat ini, Anda mendapatkan tanda centang untuk poin pintar belaka. Suka!
WendiKidd

13
Bagian yang terbaik adalah, saya masih tidak yakin kalimat mana yang berbicara tentang warna yang mana.
Brilliand

3
The view sourcediletakkan di sana oleh share-elm.com dan bukan merupakan bagian dari dikompilasi JS / HTML.
hoosierEE

1
@ ML Itu tergantung pada ruang lingkup kata "ini". Pemrogram JavaScript mengerti ...
hoosierEE

37

JavaScript dengan HTML

Saya mencoba mereproduksi komik asli lebih tepatnya. Tangkapan layar diambil menggunakan pustaka html2canvas. Angka-angka dihitung berulang kali, sehingga Anda dapat mengubah ukuran jendela atau bahkan menambahkan sesuatu ke halaman secara real time.

Cobalah online: http://copy.sh/xkcd-688.html

Berikut screenshotnya:

masukkan deskripsi gambar di sini

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

Bagus!! Cinta kesamaan dengan komik xkcd, dan fakta bahwa saya dapat mengubah teks. Rapi! : D
WendiKidd

1
pekerjaan yang mengesankan oO
izabera

Bagus ... tapi saya pikir itu harus stabil untuk menjadi "solusi". Belum dipikirkan sepenuhnya - tetapi karena belum tentu ada solusi untuk presisi yang sewenang-wenang ketika menggambar dari serangkaian mesin terbang digit terbatas, Anda harus mundur presisi jika tidak dapat diselesaikan pada presisi yang lebih tinggi kamu sedang berusaha. Saya membayangkan bahwa menggunakan font monospace yang Anda pra-hitung piksel hitam / putih akan diperlukan juga.
Dr. Rebmu

Anda menggunakan 3 warna, jadi di mana persentase abu-abu? ;)
ML

26

Memproses, 222 karakter

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

Saya selalu ingin membuat versi komik saya sendiri! Cara paling sederhana (hanya?) Yang bisa saya pikirkan untuk melakukan ini adalah coba-coba - menggambar sesuatu, menghitung, menggambar lagi ...

Program ini menerima persentase yang akurat setelah beberapa detik. Ini tidak terlalu cantik, tetapi interaktif ; Anda dapat mengubah ukuran jendela dan itu akan mulai menghitung ulang.

Menambahkan beberapa baris baru untuk keterbacaan:

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

Ini hanya menunjukkan persentase piksel putih; Karena antialiasing teks, piksel non-putih belum tentu hitam. Semakin lama berjalan semakin banyak waktu yang dibutuhkan untuk memperbarui sendiri pada ukuran.

Sunting:

Jadi, ini adalah tantangan kode; Saya semacam golf saja. Mungkin saya bisa menambahkan semacam grafik nanti, tetapi prinsip umum akan tetap sama. Interaktivitas adalah bagian yang rapi menurut saya.


Sangat bagus!! Saya pikir Anda mendapatkan kredit ekstra untuk interaktivitas; Saya senang mengubah ukuran jendela! Sangat keren :) Dan Anda adalah respons pertama saya! Saya tidak tahu apakah ada yang mau bermain, jadi terima kasih. Anda telah membuat hari saya. : D +1! (Saya penasaran, mengapa itu melambat seiring berjalannya waktu dan semakin dekat untuk mencapai persentase yang benar? Saya hanya ingin tahu apa yang terjadi, saya belum pernah melihat bahasa ini sebelumnya. Saya melihat banyak hal baru muncul di sekitar situs ini!)
WendiKidd

headdesk Kecuali Aku sengaja lupa untuk klik +1. Sekarang +1 ... haha. Maaf!
WendiKidd

1
Anda dapat menambahkan fungsi lain yang memungkinkan pengguna untuk menggambar di atasnya dengan mouse, untuk menambah interaktivitas.
AJMansfield

7
Bayangan kotak suci, Batman
Bojangles

Jika Anda ingin bermain golf, Anda bisa menggunakan background(-1)bukannyabackground(255)
Kritixi Lithos

20

Tantangan besar. Inilah solusi saya. Saya mencoba untuk sedekat mungkin dengan komik asli, saya bahkan menggunakan font xkcd .

Ini aplikasi WPF, tapi saya biasa System.Drawingmenggambar bagian karena saya malas.

Konsep dasar: Di WPF, windows are Visuals, yang artinya dapat dirender. Saya render seluruh Window contoh ke dalam bitmap, hitung hitam dan total hitam atau putih (mengabaikan abu-abu dalam font smoothing dan barang-barang) dan juga menghitung ini untuk setiap 3 gambar (untuk setiap panel). Lalu saya melakukannya lagi pada timer. Mencapai keseimbangan dalam satu atau dua detik.

Unduh:

MEGA Selalu periksa file yang Anda unduh untuk virus, dll, dll.

Anda harus menginstal font di atas ke sistem Anda jika Anda ingin melihatnya, jika tidak maka itu adalah WPF default.

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>

Kode:

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

Kode tidak dibersihkan, tetapi seharusnya agak terbaca, maaf.


2
Entri terlambat, tapi salah satu yang terbaik.
Primo

14

C (dengan SDL dan SDL_ttf): Solusi Grayscale

Berikut adalah solusi yang mengambil keuntungan dari bentuk diagram lingkaran untuk menangkap spektrum lengkap warna piksel abu-abu, mencentang di bawah 100 garis.

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

Seperti dengan solusi saya sebelumnya, jalur ke file font perlu di-hardcode pada sumbernya atau ditambahkan ke perintah build, misalnya:

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

Output dari program terlihat seperti ini:

Pie chart menunjukkan distribusi warna pixel grayscale penuh

Yang satu ini menyenangkan untuk ditonton, karena semua matematika memperlambat redraws ke tempat Anda dapat melihat program nol dalam pada solusi stabil. Perkiraan pertama mati secara liar (karena permukaannya mulai hitam pekat), dan kemudian menyusut ke ukuran akhir setelah sekitar selusin atau lebih iterasi.

Kode berfungsi dengan menghitung jumlah populasi dari setiap warna piksel pada gambar saat ini. Jika jumlah populasi ini tidak cocok dengan yang terakhir, maka ia menggambar ulang gambar. Kode ini berulang di setiap piksel, tetapi mengubah koordinat x, y menjadi koordinat polar, menghitung pertama jari-jari (menggunakan pusat gambar sebagai asal). Jika jari-jarinya berada di dalam area diagram lingkaran, itu akan menghitung theta. Theta dengan mudah diskalakan ke jumlah populasi, yang menentukan warna piksel. Di sisi lain, jika jari-jari tepat di perbatasan pai pie, maka nilai anti-alias dihitung untuk menggambar lingkaran di luar bagan. Koordinat kutub membuat semuanya mudah!


Anda sebagian besar menggunakan floatversi fungsi matematika-library, tapi kemudian tidak harus fabsmenjadi fabsf?
luser droog

Secara teknis, mungkin, tetapi fabs()lebih portabel.
kotak roti

Benar, saya punya masalah dengan yang tidak didefinisikan dalam header bahkan ketika ada di perpustakaan. Juga ada sedikit kinerja yang bisa diperoleh dibandingkan dengan transendental. :)
luser droog

10

C (dengan SDL dan SDL_ttf)

Berikut ini adalah implementasi yang sangat sederhana, di sekitar 60 baris kode 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;
}

Untuk mengkompilasi ini, Anda perlu mendefinisikan FONTPATHuntuk menunjuk ke file .ttf font yang akan digunakan:

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

Pada kebanyakan mesin Linux modern Anda dapat menggunakan fc-matchutilitas untuk mencari lokasi font, sehingga perintah kompilasi menjadi:

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

(Tentu saja Anda dapat mengganti font yang diminta dengan favorit pribadi Anda.)

Kode secara khusus tidak meminta anti-aliasing, sehingga jendela hanya berisi piksel hitam dan putih.

Akhirnya, saya terinspirasi oleh solusi elegan @ daniero untuk memungkinkan perubahan ukuran jendela. Anda akan melihat bahwa kadang-kadang program berosilasi di antara hitungan, terjebak dalam orbit di sekitar penarik yang tidak pernah bisa dijangkau. Ketika itu terjadi, ubah ukuran jendela sedikit sampai berhenti.

Dan, sesuai permintaan, inilah yang terlihat ketika saya menjalankannya di sistem saya:

Gambar ini adalah 3,36% piksel hitam dan 96,64% piksel putih.

Akhirnya, saya merasa bahwa saya harus menunjukkan, kalau-kalau ada orang di sini belum melihatnya, bahwa MAA menerbitkan wawancara dengan Randall Munroe di mana ia membahas pembuatan kartun # 688 secara rinci.


1
Solusi yang sangat bagus Bisakah Anda memasukkan beberapa screenshot dari program yang sedang berjalan, mengikuti postingan @ daniero? :)
Alex Brooks

+1, sangat bagus! Terima kasih telah menambahkan tangkapan layar :) Dan tautan wawancara menarik, terima kasih!
WendiKidd

9

masukkan deskripsi gambar di sini

Gambar adalah 100x100 dan angka-angkanya tepat, dan maksud saya tepat - saya memilih gambar 10.000 piksel sehingga persentasenya dapat dinyatakan dengan dua tempat desimal. Metodenya sedikit matematika, sedikit menebak, dan beberapa angka berderak dengan Python.

Melihat seperti yang saya ketahui sebelumnya bahwa persentase dapat dinyatakan dalam 4 digit, saya menghitung berapa banyak piksel hitam dalam setiap digit 0 hingga 9, dalam Arial setinggi 8 piksel, yang merupakan teks yang ditulis. Saya menulis sebuah fungsi cepat weightyang memberi tahu Anda berapa banyak piksel yang diperlukan untuk menulis angka tertentu, dibiarkan kosong dengan nol untuk memiliki 4 digit:

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

pxadalah array pemetaan digit ke jumlah piksel yang diperlukan. Jika B adalah jumlah piksel hitam, dan W adalah jumlah piksel putih, kami miliki B + W = 10000, dan kami perlu:

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

Dari mana konstanta itu berasal? 423 adalah "awal" jumlah piksel hitam, jumlah piksel hitam dalam teks tanpa angka. 9577 adalah jumlah piksel putih awal. Saya harus menyesuaikan jumlah piksel hitam awal beberapa kali sebelum saya berhasil mendapatkan konstanta sehingga sistem di atas bahkan memiliki solusi. Ini dilakukan dengan menebak dan menyilangkan jari saya.

Sistem di atas sangat tidak linier, jadi jelas Anda bisa melupakan penyelesaiannya secara simbolis, tetapi yang dapat Anda lakukan hanyalah mengulang-ulang setiap nilai B, atur W = 10000 - B, dan periksa persamaan secara eksplisit.

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

Mungkin lakukan gambar 250 x 400 sehingga Anda dapat mencapai 3 tempat desimal dan menampilkan lebih banyak teks untuk sementara waktu.
Joe Z.

Solusi yang sangat bagus, beberapa matematika brute force selalu dapat memecahkan masalah seperti ini!
PKC

6

QBasic

Karena nostalgia.

Dan karena saya tidak benar-benar tahu perpustakaan gambar adalah bahasa modern.

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

Metode output-count-repeat yang cukup mudah. Hal utama yang "menarik" adalah bahwa program ini secara acak mencoba berbagai ketentuan untuk persentase - saya menemukan bahwa itu tidak selalu menyatu.

Dan hasilnya (diuji pada QB64 ):

Q.Bagic metagraph


3

AWK

... dengan netpbm dan pembantu lainnya

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

Lari:

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

Gambar ditulis sebagai 'x.pbm', saya mengonversinya menjadi png untuk diunggah:

x.png

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.