Reconocimiento facial

43

El objetivo de esta tarea es identificar, limpiar y marcar todas las caras en cualquier 'imagen' dada.

¿Qué hay en una cara?

Una cara será un cuadrado ZxZ donde Z es un número entero impar mayor que 1. Las esquinas superior izquierda y derecha y el centro serán caracteres 'O', y la línea inferior será una '\' y una '/' rodeando lo suficiente '_' caracteres para llenar el resto de la línea. Ejemplos:

una cara 3x3:

O O
 O
\_/

una cara 5x5:

O   O

  O

\___/

una cara 7x7:

O     O


   O


\_____/

etc.

Entrada

La entrada estará en STDIN y consistirá en un número de cadenas de caracteres de igual longitud.

Salida

La salida debe ser la entrada con todas las caras reconocibles despejadas (es decir, todos los caracteres, excepto los ojos, la nariz y la boca deben eliminarse de los límites de la cara) y encuadrados (rodeados por caracteres +, - y |). Cuando se superponen dos o más caras, ambas deben borrarse y encuadrarse, pero se debe dar prioridad a la cara más grande (debe colocarse en la parte superior); Si ambas caras son del mismo tamaño, la prioridad queda a discreción del implementador. Si la entrada no tiene caras, la salida debe ser la misma que la entrada.

Algunos ejemplos

Entrada:

*******
*******
**O*O**
***O***
**\_/**
*******
*******

Salida:

*******
*+---+*
*|O O|*
*| O |*
*|\_/|*
*+---+*
*******

Entrada (cara incompleta):

*******
*******
**O*O**
*******
**\_/**
*******
*******

Salida:

*******
*******
**O*O**
*******
**\_/**
*******
*******

Entrada (caras anidadas):

*******
*O***O*
**O*O**
***O***
**\_/**
*\___/*
*******

Salida:

+-----+
|O   O|
|     |
|  O  |
|     |
|\___/|
+-----+

Entrada (caras múltiples):

~{$FJ*TBNFU*YBVEXGY%
FOCO$&N|>ZX}X_PZ<>}+
X$OOPN ^%£)LBU{JJKY%
@\_/$£!SXJ*)KM>>?VKH
SDY%£ILO(+{O:HO(UR$W
XVBFTER^&INLNLO*(&P:
>?LKPO)UJO$£^&L:}~{&
~@?}{)JKOINLM@~}P>OU
:@<L::@\___/GER%^*BI
@{PO{_):<>KNUYT*&G&^

Salida:

+---+*TBNFU*YBVEXGY%
|O O|&N|>ZX}X_PZ<>}+
| O |N ^%£)LBU{JJKY%
|\_/|£+-----+M>>?VKH
+---+I|O   O|HO(UR$W
XVBFTE|     |LO*(&P:
>?LKPO|  O  |&L:}~{&
~@?}{)|     |@~}P>OU
:@<L::|\___/|ER%^*BI
@{PO{_+-----+YT*&G&^

Entrada (cerca del límite):

~{$FJ*TBNFU*YBVEXGY%
OCO$&N|>ZX}X_PZ<>}+^
$OOPN ^%£)LBU{JJKY%{
\_/$£!SXJ*)KM>>?VKHU
SDY%£ILO(+{8:HO(UR$W
XVBFTER^&INLNLO*(&P:
>?LKPO)UJ^$£^&L:}~{&
~@?}{)JKOINLM@~}P>OU
:@<L::@BJYT*GER%^*BI
@{PO{_):<>KNUYT*&G&^

Salida:

---+J*TBNFU*YBVEXGY%
O O|&N|>ZX}X_PZ<>}+^
 O |N ^%£)LBU{JJKY%{
\_/|£!SXJ*)KM>>?VKHU
---+£ILO(+{8:HO(UR$W
XVBFTER^&INLNLO*(&P:
>?LKPO)UJ^$£^&L:}~{&
~@?}{)JKOINLM@~}P>OU
:@<L::@BJYT*GER%^*BI
@{PO{_):<>KNUYT*&G&^

Entrada (caras superpuestas):

~{$FJ*TBNFU*YBVEXGY%
FXC£$&N|>ZX}X_PZ<>}+
X$*OPN O%£)LBO{JJKY%
@:U%$£!SXJ*)KM>>?VKH
SDY%£OLO(+{P:HO(UR$W
XVBFTER^&IOLNLO*(&P:
>?L\___/JR$£^&L:}~{&
~@?}{)JKOINLM@~}P>OU
:@<L::@\_____/R%^*BI
@{PO{_):<>KNUYT*&G&^

Salida:

~{$FJ*TBNFU*YBVEXGY%
FX+---+-------+Z<>}+
X$|O  |O     O|JJKY%
@:|   |       |>?VKH
SD|  O|       |(UR$W
XV|   |   O   |*(&P:
>?|\__|       |:}~{&
~@+---|       |}P>OU
:@<L::|\_____/|%^*BI
@{PO{_+-------+*&G&^
Gareth
fuente
¿Qué pasa con las caras unidas (por ejemplo, donde una O funciona como ojo izquierdo y ojo derecho)? ¿Deben ser tratados como superpuestos?
Joey Adams
@Joey Adams: eso sucede en el último ejemplo.
Lowjacker
@Joey Adams @Lowjacker Sí, al igual que el último ejemplo.
Gareth
Encuentro realista la cara 3x3 y la derp de cara 7x7. Solo es mi opinión. Triste porque no tengo tiempo para ganar bountiez ... :)
tomsmeding
2
@tomsmeding Si encuentra realista la cara 3x3, odiaría ver a las personas con las que se asocia. : - \
Gareth

Respuestas:

19

Ruby, 304 298 295 caracteres

I=$<.read
q=(O=I*s=1).size
k=??+O=~/$/
o=->m,n{n.chars{|c|(m+=1)*(m%k)>0&&m<q&&O[m-1]=c}}
q.times{f=[[?\\+?_*s+?/,k*s+=1],[?O,0],[?O,s],[?O,(s+=1)/2*(1+k)]]
q.times{|x|f.all?{|a,b|I[x+b,a.size]==a}&&(o[x+k*s-1,o[x-k-1,?++?-*s+?+]]
s.times{|a|o[x+k*a-1,?|+' '*s+?|]}
f.map{|a,b|o[x+b,a]})}}
$><<O

Se prefiere la esquina inferior derecha en la superposición si las caras son del mismo tamaño. Por ejemplo, para la entrada

O.OO.O
.O..O.
\_/\_/
O.OO.O
.O..O.
\_/\_/

reconoce las cuatro caras y rinde

O |O O
 O| O
--+---
O |O O
 O| O
\_|\_/

Edición 1: como Lowjacker propuso, podemos reemplazarlo indexcon una coincidencia de expresiones regulares (-3 caracteres). Además, +1se puede compensar con un carácter ficticio adicional antes de la coincidencia, lo que ahorra otro carácter (-2 para el carácter +1, +3 para el carácter ficticio, -2 porque los corchetes ya no son necesarios). Dos más ya que podemos escribir el rango también sin corchetes.

Edición 2: Otros dos caracteres guardados reemplazando ambos ifcon &&y otro eliminando completamente el rango.

Howard
fuente
Puede usar en (O=~/$/)lugar de O.index($/)en la tercera línea (guarda 3 caracteres).
Lowjacker
@Lowjacker Gracias. Incluso podría guardar uno más con tu truco (ver mi edición).
Howard
Creo que también puede guardar 2 caracteres reemplazando las ifdeclaraciones con &&.
Lowjacker
4

Python - 1199 941

El problema me pareció bastante interesante, así que lo resolví en Python. Aquí está el código comprimido.

#!/usr/bin/env python
import fileinput,sys
m=[[c for c in l if c!='\n'] for l in fileinput.input()]
X=len(m[0])
Y=len(m)
t=[]
for n in range(3,min(X,Y)+1,2):
  for x in range(X-n+1):
    for y in range(Y-n+1):
      if m[y][x]=='O' and m[y][x+n-1]=='O' and m[y+(n//2)][x+(n//2)]=='O' and m[y+n-1][x]=='\\' and m[y+n-1][x+n-1]=='/' and "".join(m[y+n-1][x+1:x+n-1])=='_'*(n-2):
        t.append((x,y,n))
for x,y,n in t:
  def a(v,h,c):
    w=x+h; z=y+v;
    if z>=0 and z<len(m):
      if w>=0 and w<len(m[y]):
        m[z][w]=c
  for v in range(n):
    for h in range(n): 
      a(v,h,' ')
  a(0,0,'O')
  a(0,n-1,'O')
  a(n/2,n/2,'O')
  a(n-1,0,'\\')
  a(n-1,n-1,'/')
  for w in range(1,n-1):
    a(n-1,w,'_')
  for v in [-1,n]:
    for h in range(n):
      a(v,h,'-')
  for h in [-1,n]:
    for v in range(n):
      a(v,h,'|')
  a(-1,-1,'+')
  a(-1,n,'+')
  a(n,-1,'+')
  a(n,n,'+')
for l in m:
  for c in l:
    sys.stdout.write(c)
  print

Aquí está el código más legible:

#!/usr/bin/env python

import fileinput, sys

matrix = [[c for c in l if c != '\n'] for l in fileinput.input()]

max_X = len(matrix[0])
max_Y = len(matrix)

tuples = []
for n in range(3, min(max_X, max_Y)+1, 2):
  for x in range(max_X-n+1):
    for y in range(max_Y-n+1):
      # if is_face(matrix, x, y, n):
      if matrix[y][x] == 'O' and matrix[y][x+n-1] == 'O' and matrix[y+(n//2)][x+(n//2)] == 'O' and matrix[y+n-1][x] == '\\' and matrix[y+n-1][x+n-1] == '/' and "".join(matrix[y+n-1][x+1:x+n-1]) == '_'*(n-2) :
        tuples.append((x, y, n))

for x,y,n in tuples:
  # blank_and_border(matrix,x,y,n)
  def assign(dy, dx, c):
    xx = x + dx; yy = y + dy;
    if yy >= 0 and yy < len(matrix) :
      if xx >= 0 and xx < len(matrix[y]) :
        matrix[yy][xx] = c

  # blank
  for dy in range(n):
    for dx in range(n): 
      assign(dy, dx, ' ')

  # face
  assign(0, 0, 'O')
  assign(0, n-1, 'O')
  assign(n/2, n/2, 'O')
  assign(n-1, 0, '\\')
  assign(n-1, n-1, '/')
  for w in range(1,n-1):
    assign(n-1, w, '_')

  # border
  for dy in [-1,n]:
    for dx in range(n):
      assign(dy, dx, '-')

  for dx in [-1,n]:
    for dy in range(n):
      assign(dy, dx, '|')

  assign(-1, -1, '+')
  assign(-1,  n, '+')
  assign( n, -1, '+')
  assign( n,  n, '+')

for l in matrix:
  for c in l:
    sys.stdout.write(c)
  print
sgauria
fuente
2
Agregue su versión de golf sobre esta versión en su respuesta. Esta es una pregunta de código de golf y corre el riesgo de obtener votos si al menos no ha intentado jugar golf. Puedes dejar tu versión legible aquí también.
Gareth
1
Justo en, @Gareth. A menudo escribo soluciones en Java, que no es terriblemente amigable para el golf, pero siempre me tomo el tiempo para desarrollar mi solución, tanto para el ejercicio (es divertido pensar en formas de recortar caracteres y reducir la longitud total) como para satisfacer el espíritu de code-golf (que está haciendo que su solución sea lo más concisa posible). Entonces, ¡espero ver su solución de golf, sgauria!
ProgramadorDan
Gracias Gareth y @ProgrammerDan! Ese es un buen consejo: soy bastante nuevo en codegolf. He agregado mi solución de golf anterior además de la solución más larga.
sgauria