Может ли Pac-Man съесть эту строку?

46

В аркадной версии игры Pac-Man ест пакоты. Однако в этом испытании он жаждет буквенно-цифровых символов и знаков препинания в строке.

Ваша задача - создать функцию, которая будет кормить Pac-Man строкой, оценивать, сможет ли он ее съесть или нет, и возвращает строку с указанием местоположения Pac-Man.

Pac-Man ( <) поедает персонажей слева направо, оставляя подчеркивание или пробел для каждого персонажа по ходу, и его цель - добраться от первой позиции-1 до последней позиции + 1:

1. <Pac
2. _<ac
3. __<c
4. ___<

Тем не менее, естественный враг Pac-Man, призрак, остановит его, если он встретит одну из букв в слове «GHOST» (без учета регистра). Ваша функция должна возвращать строку с местоположением Pac-Man, когда он встречает ghostперсонажа:

1. <No!
2. _<o!

Единственное, что может победить призрака - это гранула. Если Pac-Man достигнет буквы в слове «ПЕЛЛЕТ» (также не чувствительной к регистру) перед тем, как прийти к призраку, он съест призрака и продолжит движение, и этот шарик будет израсходован. Силовые пеллеты могут складываться (то есть в ppggобоих призраков можно было бы съесть). Символ Т существует как призрак и шарик, поэтому его можно игнорировать (например, любую другую букву a).

1. <Pop
2. _<op
3. __<p
4. ___<

Для дальнейшего уточнения, в строке «Pac-Man проигрывает здесь» выполняются следующие операции:

P <P, +1 Pellet (1 pellet)
a <a
c <c
- <-
M <M
a <a
n <n
  <[space]
l <l, +1 Pellet (2 pellets)
o <o, -1 Pellet (1 pellet)
s <s, -1 Pellet (0 pellets)
e <e, +1 Pellet (1 pellet)
s <s, -1 Pellet (0 pellets)
  <[space]
h <h, ghost wins, returns
e
r
e

Примеры

Input: Pacman wins!
Output: ____________<

Input: Pacman loses wah-wah :(
Output: _______________<h-wah :(

Input: PELLET PELLET GHOST
Output: ___________________<

Input: Hello World!
Output: <Hello World!

Input: <_!@12<_<_<
Output: ___________<

Это код-гольф - выигрывает самая низкая оценка в байтах.

atlasologist
источник
29
То есть у гранул нет срока годности?
Rɪᴋᴇʀ
Конечные таблицы принимаются в выходных данных?
Катенкё
7
+1 за то, что «здесь» - то, где pacman проигрывает. Умный тестовый кейс.
Оливье Дюлак
5
> [В этом вызове он жаждет буквенно-цифровых символов и знаков препинания в строке. ... Yacc -man?
Каз
9
Теперь я вижу замаскированного серого пакмана с черными губами каждый раз, когда я смотрю на <символ ...
QBrute

Ответы:

16

Желе, 34 33 байта

Œl“ʋʋ“ṁḍ»ċ€Ð€IF+\‘0ṭi0ð’”_×;”<;ṫ@

Попробуйте онлайн!

Я думаю, что я наконец начинаю понимать Jelly. Чувствует себя немного страшно.

PurkkaKoodari
источник
5
Я бы начал волноваться, когда ты сможешь читать бегло :)
quetzalcoatl
30

Retina , 55 38 байт

i`^(([elp])|[^ghos]|(?<-2>.))*
$.&$*_<

Попробуйте онлайн! (Первая строка позволяет запускать сразу несколько тестовых случаев.)

объяснение

Проблема заключается в том, чтобы найти самый длинный префикс, который не имеет непревзойденных закрывающих скобок. Кроме того, что мы можем использовать либо e, lили pв месте , (и либо g, h, oили sна месте ).

Следовательно, это решение является почти учебным примером балансировки групп. Я не буду вдаваться в подробности того, как они работают, так как этот код по сути такой же, как и стандартный пример, который вы можете прочитать в моем SO-ответе о балансировке групп .

Поэтому вся программа представляет собой одну подстановку регулярных выражений. iАктивирует прецедентное нечувствительность. Затем мы либо сопоставляем шарик с [elp]и увеличиваем счетчик глубины (в виде стека захвата группы 2), либо сопоставляем что-то, что не является призраком, [ghos]либо сопоставляем призрак с .и уменьшаем счетчик глубины, выталкивая из стека 2, Конечно, в принципе это позволяет сопоставить шарик с [^ghos]разделом или не-призрак с .разделом, но благодаря жадному сопоставлению и способу обратного выражения регулярного выражения, эти возможности никогда не используются движком регулярного выражения.

Затем подстановка использует две особенности Retina: $*повторяет символ справа столько раз, сколько указано токеном слева. Этот токен - $.&это длина всего матча. Это просто означает, что мы заменяем каждый символ в матче на _. И затем мы также добавляем <к этим подчеркиваниям. Та часть ввода, которая не была съедена, остается незатронутой подстановкой.

Мартин Эндер
источник
Хорошее злоупотребление захватом групп!
Дрянная Монахиня
11
@LeakyNun Злоупотребление? Для этого созданы балансировочные группы. : D
Мартин Эндер
1
Эй, смотри, ответ Retina, который отдаленно похож на регулярное выражение, которое я использую
кошка
10

Python 2, 114 113 108 байт

s=raw_input()
p=i=0
for c in s:
 p+=(c in'plePLE')-(c in'ghosGHOS')
 if p<0:break
 i+=1
print'_'*i+'<'+s[i:]
Arfie
источник
Ваша функция возвращает None, а не ответ. А как ты считаешь 107? Я считаю 110.
Stefan Pochmann
@StefanPochmann двойные пробелы являются табуляциями, и это позволяет печатать ответ, а не возвращать его
Blue
@muddyfish Ах, спасибо. Они, кажется, здесь не являются вкладками, даже когда я иду «редактировать». И проблема ясно гласит: «возвращение» ... существуют ли правила для всего сайта, отменяющие это или около того? (Я здесь новенький и не знаю)
Stefan Pochmann
Вкладки @StefanPochmann съедаются SE (обычно преобразуются в 4 пробела). Если явно не указано, печать в функции разрешена. ОП, вероятно, не хотел переопределять это
Blue
Я думаю, что разумно сказать, что он всегда должен либо возвращать, если это функция, либо читать из stdin и печатать. Я перейду к чтению со стандартного ввода, которое в любом случае должно быть короче.
Арфи
8

Python 2, 89 байт

Иногда моя упрямая решимость сделать Python функциональным языком имеет свои преимущества.

def f(s,l=1):l+=(s[:1]in'plePLE')-(s[:1]in'ghosGHOS');return s*l and'_'+f(s[1:],l)or'<'+s

(Слегка) ungolfed:

def f(s, l=1):
    l += (s[:1] in 'plePLE') - (s[:1] in 'ghosGHOS')
    return (s * l) and ('_' + f(s[1:], l)) or ('<' + s)

Создает строку результата, используя рекурсию. Обновление l(для «жизней») добавляет 1 для pellets ( True - False == 1), вычитает 1 для ghosts ( False - True == -1) и добавляет 0 для любого другого персонажа. Он также добавляет 0, когда sэто пустая строка, благодаря разрезанию в Python и тому факту '' in any_str == True, что пеллет и призрак отменяются.

Оператор возврата используется test and b or aвместо того, a if test else bчтобы сохранить один байт. Базовый случай рекурсии происходит, когда либо заканчивается строка, либо у Pac-Man заканчиваются шарики, кратко представленные как s*p, что равно ''(и поэтому оценивается как ложное), когда либо либо, s == ''либо p == 0.

beiju
источник
8

C #, 269 256 232 212 211 байт

Первый в истории пост здесь, так что это, вероятно, намного дольше, чем могло бы быть (и, вероятно, потому что это в C #). Любые советы о том, где я мог бы сократить это было бы здорово!

Спасибо всем в комментариях, кто мне помог!

Гольф версия

static void p(string x){int p=0,i=0;string t='<'+x;var s=t.ToCharArray();for(;++i<s.Length;){if("PELpel".Contains(s[i]))p++;if("GHOSghos".Contains(s[i])&&--p<0)break;s[i]='<';if(i>0)s[i-1]='_';}Console.Write(s);}

Неуправляемая версия

static void p(string x) {
 int p = 0, i = 0;
 string t = '<' + x;
 var s = t.ToCharArray();
 for (; ++i < s.Length;) {
  if ("PELpel".Contains(s[i])) p++;
  if ("GHOSghos".Contains(s[i]) && --p < 0) break;
  s[i] = '<';
  if (i > 0) s[i - 1] = '_';
 }
 Console.Write(s);
}
Андрей
источник
1
Вы можете заменить типы в переменных переменной с помощью ключевого слова var. например, var temp = '' + input; Цикл for может быть переписан для сохранения 4 символов: for (var i = 0; i ++ <s.Length;)
CSharpie
1
Вы можете использовать запятые для объявлений "int i = 0, p = 0; строка P =" PELpel ", G =" GHOSghos ", t = '' + x;" и изменение от @CSharpie, делая цикл "for (; i ++ <s.Length;)". Кроме того, вы можете "Console.Write (s);" непосредственно на общую сумму 235 байтов.
Никсон
1
Я думаю, что это также должно работать без elseсохранения еще 5 символов. И запустив цикл у i = 1вас должна быть возможность удалить последний if, так как код может выполняться каждый раз.
Frozn
1
Вы можете избавиться от своей декларации c и просто вставить s[i]доступ для 5 символов.
ДжастинМ - Восстановить Монику
1
Стоит ли назначать P="PELpel"и G="GHOSghos"? Вы используете их только один раз каждый. Я что-то упустил или это просто 4 лишних символа? Кроме того, вам нужно else? "PELpel".Contains(c)и "GHOSghos".Contains(c)должен быть взаимоисключающим.
jpmc26
7

Pyth, 53 48 44 байта

4 байта спасибо @ Pietu1998 за трюк !!@-> }(который могут понять только люди, которые знают Pyth)

++ * Jf <@ + sM._m - !! @ d "PELpel" !! @ d "GHOSghos" Q_1T00 \ _ \ <> QJ 
++ * Jf <@ + sM._m - !! @ d "PEL" !! @ d "Ghos" rQ1_1T00 \ _ \ <> QJ
++ * Jf <@ + sM._m-} d "PEL"} d "Ghos" rz1_1T00 \ _ \ <> ZJ

Тестирование.

Дрянная Монахиня
источник
17
что понимают только люди, знающие Pyth. Ну, как и весь остальной код, естественно
Луис Мендо
4
@LuisMendo Чтобы быть справедливым по отношению к людям, которые не знают Pyth, я почти уверен, что большинство из них может понять, что пересечение множества между одним одиночным множеством и другим множеством, имеющим какие-либо члены, эквивалентно тому, что член одиночного набора является членом больший набор: P
FryAmTheEggman
1
@FryAmTheEggman, очевидно, !!@это просто триграф }, верно? : p
CAD97
7

MATL , 37 36 35 байт

tkt'ghos'mw'pel'm-Ys1=Y>&)g95*60bhh

Попробуйте онлайн!

объяснение

tkt      % Input string implicitly. Duplicate, convert to lower case, duplicate
'ghos'm  % True for "ghost" characters
w'pel'm  % Swap to bring lowercase copy to top. True for "pellet" characters
-Ys      % Subtract, cumulative sum. Pac-Man can reach until the first "1"
1=       % True for entries that equal 1
Y>       % Cumulative maximum. This gives false until the first true is found, and
         % true from there on
&)       % Split original string in two parts, given by the zeros and ones respectively
g95*     % Convert the first part into ones and multiply by 95. This gives a numerical
         % array containing number 95 (ASCII for '_')
60       % Push 60 (ASCII for '<')
b        % Bubble up second part of original string
hh       % Concatenate the three strings/arrays, automatically converting to char
Луис Мендо
источник
7

JavaScript (ES6), 98 байт

s=>s.replace(/./g,c=>p<0?c:(p+=/[elp]/i.test(c)-/[ghos]/i.test(c))<0?"<"+c:"_",p=0)+"<".slice(p<0)

Пояснение: pподдерживает текущее количество гранул. Если он уже отрицательный, мы просто возвращаем символ и продолжаем, так что остальная часть строки остается нетронутой. В противном случае мы проверяем текущий символ, и если он pстановится отрицательным, мы вставляем <символ, в противном случае мы заменяем текущий символ на _. Наконец, если pникогда не становится отрицательным, мы добавляем суффикс a <к строке.

Нил
источник
4

Pyth, 47 46 44 байта

++*\_Kh+f!h=+Z-}Jr@zT0"pel"}J"ghos"Uzlz\<>zK

Попробуйте онлайн. Тестирование.

Совсем иной подход, чем у Лики Нун, и я совершенно уверен, что это можно сделать в дальнейшем.

PurkkaKoodari
источник
Используйте Zвместо Gи измените f!наf!h
Leaky Nun
@LeakyNun Только что понял это и в другой вкладке. Благодарю.
PurkkaKoodari
2
Я думаю, что tв "ghost"должен быть удален
Leaky Nun
Если мы будем продолжать играть в наши решения, в чем разница между нашими решениями?
Утренняя монахиня
@LeakyNun Я не уверен, что из этого ближе, но моя первая попытка дала мне 43 байта , и я не думаю, что мне нужно добавлять третий ответ. Может быть, мы должны работать вместе в чате Pyth?
FryAmTheEggman
4

Луа, 198 190 184 185 163 байта

Хорошо, я признаю, это долго. Очень долго. У Lua есть некоторые инструменты для работы со строками, но он ограничен, то же самое касается условных выражений, которые занимают много места.

Редактировать: спасибо @LeakyNun за спасение мне 9 байтов :) Потерял несколько байтов, чтобы исправить ошибку

Редактировать 2: 163 байта решение найдено @LeakyNun

i=0p=0n=...for c in n:gmatch"."do
p=p+(c:find"[ghosGHOS]"and-1or c:find"[pelPEL]"and 1or 0)if p<0then
break else i=i+1 end end print(('_'):rep(i)..'<'..n:sub(i+1))

Старый 185

p=0z=(...):gsub(".",function(c)p=p+(c:find"[ghosGHOS]"and-1or
c:find"[pelPEL]"and 1or 0)s=p<0 and 1or s
return s and c or'_'end)_,i,s=z:find"(_+)"print((s or'')..'<'..z:sub(1+(i or 0)))

Ungolfed

i=0                        -- number of characters eaten
p=0                        -- pellet counter
n=...                      -- shorthand for the argument
for c in n:gmatch"."       -- iterate over each characters in the input
do
  p=p+(c:find"[ghosGHOS]"  -- if the current char is a GHOST
        and-1              -- decrement the pellet counter
      or c:find"[pelPEL]"  -- if it's a PELLET
        and 1              -- increment it
      or 0)                -- else, leave it alone
  if p<0                   -- if we try to eat a ghost without pellet
  then 
    break                  -- stop iterating
  else
    i=i+1                  -- else, increment our score
  end
end

print(('_'):rep(i)         -- print i*'_'
  ..'<'                    -- appended with Pacman
  ..n:sub(i+1))            -- appended with the remaining characters if we died
Katenkyo
источник
Удалить d=c:lower()и искать заглавные буквы, а также
Leaky Nun
and 1or s and 1or s s and s
Утренняя монахиня
@LeakyNun не видел, что было бы короче просто написать все письма ... Спасибо. Кроме того, во втором комментарии упоминается кое-что, что я изменил, но только в некольфе> _ <
Katenkyo
print(('').rep('_',i)..','..z:sub(i+1))
Утренняя монахиня
@LeakyNun Я работаю над подобным решением, но проблема заключается в том, что это iможет бытьnil
Katenkyo
3

Python 3, 176 157 150 149 134 133 124 байта

Определите функцию с именем, fкоторая принимает строку в качестве аргумента

def f(s):
 n=i=0
 for c in s:
  if c in"GgHhOoSs":
   if n:n-=1
   else:break
  n+=c in"PpEeLl";i+=1
 return"_"*i+"<"+s[i:]

Может быть, больше в гольф

Спасибо всем, кто прокомментировал: D

TuxCrafting
источник
1
﹐ Удалить x=c.upper()и найти строчные буквы
Leaky Nun
Вы можете сохранить некоторые из них, написав несколько выражений в одной строке, разделив их, ;а не располагая каждое в отдельной строке. Также вы можете использовать Python 2, который позволяет использовать пробелы в качестве первого целевого уровня и вкладки в качестве второго.
Денкер
n=i=0не n=0а i=0. t[i]="_"вместо того t[i] = "_"же для t[i] = "<". return''.join(t), удали это место.
Эрик Outgolfer
@LeakyNun В тестовых случаях прописные буквы.
TuxCrafting
@ TùxCräftîñg Нет, они имеют в виду "GgHhOoSs"и "PpEeLl".
Эрик Outgolfer
2

Python 3, 114 110 байт

Мой первый код гольф.

Спасибо Dr Green Eggs и Iron Man за сохранение 4 байтов.

l,x=1,0
f,y,s="ghosGHOS","pelPEL",input()
while s[x:]*l:l+=(s[x]in y)-(s[x]in f);x+=l>0
print("_"*x+"<"+s[x:])

Использует вычисление логических значений в единицу и ноль для сжатия логического И для умножения. (0 * 0 = 0, 1 * 0 = 0, 1 * 1 = 1). Я надеюсь, что это хорошая первая попытка.

Разрушаемый Лимон
источник
Приятный ответ и добро пожаловать на сайт! Какую версию Python вы используете? Вы можете указать это. Кроме того, я не проверял это, но вы могли бы сделать это, чтобы удалить while s[x:]*l4 байта.
DJMcMayhem
1

Powershell, 185

{$l=1;$o="";for($i=0;($i -lt $_.Length) -or (($o+="<") -and 0); $i++){if ($_[$i] -match '[pel]'){$l++}if($_[$i] -match '[ghos]'){$l--}if(!$l){$o+="<"+$_.substring($i);break}$o+="_"}$o}

Ungolfed:

("Pacman wins!",
"Pacman loses wah-wah :(",
"PELLET PELLET GHOST",
"Hello World!"
) | 
% {
    $l=1;$o="";
    for($i = 0; ($i -lt $_.Length) -or (($o+="<") -and 0); $i++) {
        if ($_[$i] -match '[pel]') { $l++ }
        if ($_[$i] -match '[ghos]') { $l--}
        if (!$l) { $o+="<"+$_.substring($i); break }        
        $o += "_"
    }
    $o
}
Эрис
источник
1

Python3, 211 184 байта

Аргумент 's' является строкой

def f(s):
    p=c=0
    for i in s:
        if i in "gGhHoOsS":
            if p<1:break
            else:p-=1
        if i in "pPeElL":p+=1
        c+=1
    return"_"*c + "<" + s[c:]

Буду признателен за советы по игре в гольф, так как это моя первая попытка игры в гольф

Спасибо за комментарий :)

Исаак Грин
источник
2
Добро пожаловать в Программирование Пазлов и Code Golf! Несколько советов: Есть много ненужных пробелов между операторами. Удаление их сэкономило бы большое количество байтов. Также вы можете использовать Python 2, который позволяет использовать пробелы в качестве первого целевого уровня и вкладки для остальных.
Денкер
1
Вы можете заменить первое return "_"*c + "<" + s[c:]на просто breakкак этот код будет выполняться после цикла for в любом случае.
Арфи
Попробуйте онлайн! Кстати, я получил 183 байта. Есть ли последний символ перевода строки, который вы посчитали?
Павел
1

Haskell, 119 113 байт

Спасибо Даниэлю Вагнеру за 6 байтов меньше.

p=(0!)
n!(c:s)|elem c"ghosGHOS"=if n<1then '<':c:s else(n-1)&s|elem c"elpELP"=(n+1)&s|0<1=n&s
_!_="<"
n&s='_':n!s

Назовите это как p "Hello World!".

Это 1thenкрайний случай, который правильно интерпретируется в моем GHC (7.10), но он отбрасывает большинство подсветок синтаксиса. Так что это может интерпретироваться по-разному и в вашем компиляторе.

Ungolfed:

pacman string = go 0 string

-- | In the golfed version: (!)
go _   []                   = "<"                            -- won
go pellets (char:string)
 | char `elem` "ghosGHOS"
 = if pellets < 1        then '<':char:string                -- lost
                         else nextStep (pellets - 1) string  -- ghost
 | char `elem` "elpELP"
 =                            nextStep (pellets + 1) string  -- pellet
 | otherwise
 =                            nextStep  pellets      string  -- anything else

-- | In the golfed version: (&)
nextStep pellets string = '_':(go pellets string)
MarLinn
источник
1
Вы можете сэкономить несколько байтов, поместив всех охранников в одну строку, например n!(c:s)|elem c"blah"=blah|elem c"blah"=blah|0<1=blah.
Даниэль Вагнер
@ Даниэль Вагнер Хороший совет, спасибо!
MarLinn
Можете ли вы добавить ссылку TIO ? Я продолжаю получать ошибки, когда я пытаюсь заставить это работать.
Павел
1

C 237 байт

#include<stdio.h>
#include<string.h>
main(p,i,j){char s[99];fgets(s,99,stdin);for(p=i=0;s[i];++i){if(strchr("GHOSghos",s[i])){if(p)p--;else break;}else if(strchr("PELpel",s[i]))p++;}j=i-(s[i]==0);while(j--)printf("_");printf("<%s",s+i);}
user2064000
источник
1

C ++, 315 373 327 байт

(Примечание: все еще игра в гольф)

#include <iostream>
#include <string>
using namespace std;
int main(){string input;getline(cin, input);
if(input.find("Pac-Man loses")!=string::npos||input.find("Pacman loses")!=string::npos)
    cout<<"<"<<input.substr(15,input.length()-1);
else{for(unsigned i=0;i<=input.length();++i)
    cout << "_";
cout<<"<";
}return 0;
}
tachma
источник
1
Pac-Man не проигрывает, когда это должно быть.
tildearrow
Привет @tildearrow, спасибо за просмотр моего кода! Я обновлю свой пост.
Тачма
Я думаю, что это может быть в гольфе больше. Попробуйте удалить / перевод строки пробелов после if(), и удаления пробелов вокруг !=, ||, =, -, и <=. Кроме того, не cin>>inputработает вместо getline? Вы также можете сгущаться вокруг ;.
NoOneIsHere
@ NoOneIsHere, спасибо за ваш комментарий! На самом деле я новичок в коде-гольфе, поэтому я попытаюсь еще немного улучшить свой код и обновить свой пост. Если у вас есть другие полезные советы по игре в код, я буду очень признателен.
Тачма
1
Вы можете посмотреть на Советы по игре в гольф на C / C ++ .
NoOneIsHere
1

Рубин (119 байт)

q=i=0;a=$**" ";a.split(//).each{|c|q+=((c+?p=~/[ple]/i)^1)-((c+?g=~/[ghos]/i)^1);q<0?break : i+=1};p ?_*i+?<+a[i..-1]

Вероятно, есть некоторые вещи, которые я пропускаю, поскольку я новичок в этом ...

Руби мой друг :)

pottedmeat7
источник
1
Добро пожаловать в PPCG!
FlipTack
0

Perl, 54 (52 + 2) байта

s/([pel](?1)*[ghos]|[^ghos
])*/'_'x(length$&).'<'/ei

Необходимо -pуказать в параметрах командной строки.

Объяснение:

-pОпция заставляет заявление быть обернуты в цикле чтение-модификация-печати, где во время каждой итерации цикла, $_содержит строку ввода, в том числе линии разделителя.

Регулярное выражение в значительной степени та же идея, что и в ответе Retina.

Назовите шаблон поиска ([pel](?1)*[ghos]|[^ghos ])*"приемлемым". Тогда это может быть рекурсивно определено как:

Строка является «приемлемой», если:

  • Это символ в « PELLETкроме» T, за которым следует допустимая строка, за которой следует символ в « GHOSTкроме» T.
  • Это не символ, GHOSTза исключением того, Tчто это не символ перевода строки.
  • Это объединение любого числа (включая 0) допустимых строк.

Это определение допускает больше шариков, чем призраков: PELперсонаж может быть сопоставлен либо с шариком, либо с не призрачным персонажем.

Пустая строка считается приемлемой, поэтому регулярное выражение гарантированно совпадает в позиции 0, где будет найдена самая длинная приемлемая подстрока.

Эту самую длинную приемлемую подстроку затем сопоставляют подчеркивания равной длины, за которым следует <.

HVD
источник
Флаги iirc, такие как -p, считаются по одному байту.
Павел
1
@Pavel Это сложно. Если обычный вызов без -pиспользования -, например, perl -e-> perl -pe, то -это бесплатно. Но я думаю, что perl -eверсия длиннее из-за цитирования, поэтому я не могу использовать это здесь.
HVd