Conversor de cores RGB para Xterm

13

Os terminais compatíveis com Xterm de 256 cores adicionam 240 cores além das 16 cores usuais do sistema. As cores 16-231 usam 6 níveis (0, 95, 135, 175, 215, 255) de vermelho, verde e azul, ordenados lexicograficamente. As cores 232-255 são simplesmente 24 níveis de cinza (8 ... 238 por 10s). Para ter uma idéia melhor do que estou falando, consulte esta tabela .

O desafio

Seu objetivo é criar um programa ou função que receba, como entrada, valores rgb e produza o número correspondente à cor Xterm mais próxima desse valor rgb. Como as 16 cores do sistema (cores de 0 a 15) geralmente são personalizáveis, você as excluirá dessa conversão.

Para definir melhor qual é a cor "mais próxima", use a distância de Manhattan ao longo dos componentes vermelho, verde e azul. Por exemplo, rgb(10, 180, 90)está a 20 unidades de distância rgb(0, 175, 95)(cor 35) porque abs(10 - 0) + abs(180 - 175) + abs(90 - 95) == 20. Se a cor de entrada estiver igualmente entre duas ou mais cores Xterm, imprima a cor Xterm com o índice mais alto.

Exemplos

 R   G   B     Xterm
  0   0   0 ==> 16
 95 135   0 ==> 64
255 255 255 ==> 231
238 238 238 ==> 255

 90 133 140 ==> 66
218 215 216 ==> 188
175 177 178 ==> 249

175   0 155 ==> 127
 75  75  75 ==> 239
 23  23  23 ==> 234
115 155 235 ==> 111

Regras

  • As brechas padrão são proibidas
  • Seu programa ou função pode receber valores de rgb em qualquer formato razoável, incluindo:
    • Argumentos separados para vermelho, verde e azul
    • Uma lista, tupla, dicionário ou similar
    • String ou stdin separados por delimitadores
    • Cores hexagonais (por exemplo #ff8000)
  • Você pode assumir que todos os valores de r, g e b serão números inteiros entre 0 e 255.
  • Como as 16 cores do sistema devem ser excluídas do mapeamento, todas as saídas devem estar na faixa de 16 a 255.

Isso é , então o código mais curto vence.

Beefster
fonte

Respostas:

4

Haskell , 132 bytes

v=0:[95,135..255]
f c=snd$maximum[(-sum(abs<$>zipWith(-)c x),i)|(i,x)<-zip[16..]$[[r,g,b]|r<-v,g<-v,b<-v]++[[g,g,g]|g<-[8,18..238]]]

Experimente online!

Recebe a entrada como uma lista de números inteiros [red, green, blue].

Implementação bastante direta. Primeiro, construo uma lista das cores Xterm que estamos usando com duas compreensões de lista concatenadas juntas. O primeiro deles lida com as cores 16 a 231, repetindo a iteração tripla sobre o vque contém os valores que essas cores usam. O segundo apenas repete os valores de cinza e os coloca nos três slots. Então eu o indexo com zip (começando em 16) e faço um par com a distância de manhattan (negada) e esse índice e tomo o máximo. Eu usei o máximo porque estamos empatando no maior índice e, dessa forma, me economiza um extra -.

user1472751
fonte
3

Ruby , 280 180 166 164 164 155 bytes

->c{d=0,95,135,175,215,255
a=(0..239).map{|n|n<216?[d[n/36],d[(n%36)/6],d[n%6]]:[n*10-2152]*3}.map{|t|t.zip(c).map{|a,b|(a-b).abs}.sum}
a.rindex(a.min)+16}

Experimente online!

Um lambda tomando a cor de entrada como uma matriz de números inteiros.

Eu tive mais problemas para gerar as cores Xterm do que eu esperava! Estou preparado para ser derrotado de maneira embaraçosamente modesta nessa área. Eu usei a conversão de base como um tipo de compactação, mas a única maneira que sei fazer isso no Ruby é através daInteger#to_s qual é um pouco estranho.

-100 bytes: leia o problema com mais cuidado e ignore as 16 cores do sistema ^ _ ^;

-14 bytes: use a conversão da base manual em vez de .to_s(6)

-2 bytes: ignore colchetes ao declarar matriz

-9 bytes: Crie uma lista de cores Xterm com apenas uma map; isso também salva um sinal de mais e um par de parênteses.

->c{
  d=0,95,135,175,215,255                 # d is the set of possible RGB values
  a=(0..239).map{|n|                     # Create the array of Xterm triplets
    n<216 ? [d[n/36],d[(n%36)/6],d[n%6]] # Convert x from base 6 to base d, or
          : [n*10-2152]*3                #   create a uniform triplet
  }.map{|t|
    t.zip(c).map{|a,b|(a-b).abs}.sum     # Map from triplets to Manhattan distance
  }
  a.rindex(a.min) +                      # Find the last index of the lowest distance
  16                                     # Offset for the exluded system colors
}
benj2240
fonte
1
BTW, você não precisa mapear para as 16 cores do sistema. Talvez eu deva deixar isso mais claro na descrição.
Beefster
Ooh, isso vai me ajudar um pouco! Definitivamente, senti falta disso na redação da pergunta original.
benj2240
1

Kotlin , 299 290 267 265 bytes

(16..255).associate{it to if(it<232)(it-16).let{i->listOf(0,95,135,175,215,255).let{l->listOf(l[i/36],l[(i/6)%6],l[i%6])}}else(8..238 step 10).toList()[it-232].let{listOf(it,it,it)}}.minBy{(k,v)->(it.zip(v).map{(a,b)->kotlin.math.abs(a-b)}.sum()*256)+(256-k)}!!.key

Embelezado

(16..255).associate {
    it to if (it < 232) (it - 16).let { i ->
            listOf(0, 95, 135, 175, 215, 255).let { l ->
                listOf(
                        l[i / 36],
                        l[(i / 6) % 6],
                        l[i % 6])
            }
        } else (8..238 step 10).toList()[it - 232].let { listOf(it, it, it) }
}.minBy { (k, v) ->
    (it.zip(v).map { (a, b) -> kotlin.math.abs(a - b) }.sum() * 256) + (256 - k)
}!!.key

Teste

data class Test(val r: Int, val g: Int, val b: Int, val out: Int)

val test = listOf(
        Test(0, 0, 0, 16),
        Test(95, 135, 0, 64),
        Test(255, 255, 255, 231),
        Test(238, 238, 238, 255),

        Test(90, 133, 140, 66),
        Test(218, 215, 216, 188),
        Test(175, 177, 178, 249),

        Test(175, 0, 155, 127),
        Test(75, 75, 75, 239),
        Test(23, 23, 23, 234),
        Test(115, 155, 235, 111)
)
fun z(it:List<Int>): Int =
(16..255).associate{it to if(it<232)(it-16).let{i->listOf(0,95,135,175,215,255).let{l->listOf(l[i/36],l[(i/6)%6],l[i%6])}}else(8..238 step 10).toList()[it-232].let{listOf(it,it,it)}}.minBy{(k,v)->(it.zip(v).map{(a,b)->kotlin.math.abs(a-b)}.sum()*256)+(256-k)}!!.key

fun main(args: Array<String>) {
    for (i in test) {
        val r = z(listOf(i.r, i.g, i.b))
        println("$i ${i.out} ==> $r")
    }
}

TIO

TryItOnline

jrtapsell
fonte
1

Lote, 266 bytes

@set/ax=15,m=999
@set s=for %%b in (0 95 135 175 215 255)do @
@%s:b=r%%s:b=g%%s%call:c %* %%r %%g %%b
@for /l %%g in (8,10,238)do @call:c %* %%g %%g %%g
@echo %n%
:c
@set/ax+=1,r=%4-%1,g=%5-%2,b=%6-%3
@set/ad=%r:-=%+%g:-=%+%b:-=%
@if %d% leq %m% set/an=x,m=d
Neil
fonte
1

Stax , 41 bytes

¬ÿ▒ú╘Σt∙Æ9φ☻ùí&BQq═IÜH∩Å╧♥f⌠óH]╨⌡≤@■Q‼Hr¼

Execute e depure online!

Versão ASCII de 50 bytes:

"4GOW_g"{52-5*m3|^24{A*vv]3*m+{;\{E:-m|+mc|m|IH16+
Weijun Zhou
fonte
1

C (gcc), 202 192 157 150 (141 com erros) 138 134 bytes

l,m,t,i;a(c,x){x=abs(c-=i>215?i*10-2152:x*40+!!x*55);}f(r,g,b){for(i=l=240;i--;t=a(r,i/36)+a(g,i/6%6)+a(b,i%6),t<l?l=t,m=i:1);i=m+16;}

Obrigado @ceilingcat

Experimente online!

PrincePolka
fonte
1
O problema não é definido pelos testes (que são claramente rotulados como exemplos); portanto, adicione um novo teste.
Ton Hospel
1
@TonHospel I reparou o erro agora e reduzir -3 bytes, mas obrigado pela resposta
PrincePolka