Міста: пам'ятки


18

Я в положенні (0, 0) нескінченного двовимірного міста, яке ідеально поділяється на блоки, по центру в кожній точці грат, деякі з яких містять будинки. Будівля у певній точці (x, y) займає всю площу з протилежними кутами в (x-.5, y-.5) та (x + .5, y + .5) , включаючи її межу. Будівля видно, якщо є деякий відрізок лінії від (0, 0) до точки в будівлі, яка не перетинає жодну іншу будівлю.

Наприклад, я (the @) можу побачити 6 будівель ( *) у наступному місті:

  *
 *
*
*@
x**
 *  y

Я не бачу будівлі, позначеної символом (x в (-1, -1), оскільки його перешкоджають дві сусідні до неї; або один , відмічені зірочкою yв (3, -2) , тому що він наштовхується на краю (1, -1) будівлі.

Вхідні дані

Багаторядковий рядок або список рядків, необов'язково прокладених пробілами в прямокутник. Він буде містити лише:

  • один @(моя позиція)
  • Пробіли
  • *, які представляють будівлі.

Завжди буде хоча б одна будівля, а отже, принаймні одна видима будівля.

Вихід

Кількість видимих ​​будівель.

Тестові справи

*@
1

* *******
 @     * 
7

*****
**@**
*****
4

   *
  **
@ **
2

*      *
 *    * 
@
4

@
 *
  ***
1

Дякуємо @Geobits за назву .



Щодо тестового випадку 3, він оточений 8 *, але результат - 4. Але такі кути, схоже, не перекриті іншими будівлями. Чи є правило не включати кути?
LukStorms

1
@LukStorms Уявіть, що кожна зірка насправді є кубами, як у Minecraft. Якби ви стояли посеред цього, ви могли б бачити лише 4 блоки
Blue Blue

Невже ви будете так люб’язно зачекати, перш ніж я ввійду в рішення про гольф (дуже скоро), перш ніж нагородити щедрістю? :)
Лейф Віллерс

Відповіді:


4

Unity + C #, 589 байт

Це, мабуть, найгірша мова, щоб зробити гольф з кодом (читати: гірше, ніж на Java), але Unity пропонує багато корисних функцій для цього завдання.

EDIT: пропущено пару пробілів, повертає довжину списку, а не лічильник

Гольф:

using UnityEngine;using System.Collections;public class c:MonoBehaviour{public int h(string[]i){ArrayList k=new ArrayList();for(int y=0;y<i.Length;y++){char[]l=i[y].ToCharArray();int x=0;foreach(char c in l){if(c=='*'){GameObject b=GameObject.CreatePrimitive(PrimitiveType.Cube);b.transform.position=new Vector3(x,y);}if(c=='@')transform.position=new Vector3(x,y);x++;}}for(int n=0;n<3600;n++){RaycastHit h;Physics.Raycast(transform.position,Quaternion.Euler(0,0,n/10)*Vector3.up,out h);if(h.collider!=null){GameObject o=h.collider.gameObject;if(!k.Contains(o))k.Add(o);}}return k.Count;}}

Безголівки:

using UnityEngine;
using System.Collections;

public class citiessightlines : MonoBehaviour {

    public ArrayList todelete;   // Anything concerning this array just has to do with cleanup of 
                                 //objects for testing, and doesn't contribute to the byte count.
    void Start()
    {
        todelete = new ArrayList();
    }
    public int calcSight(string[]input)
    {
        todelete = new ArrayList();
        int total = 0;
        ArrayList check = new ArrayList();
        for (int y=0;y < input.Length; y++)
        {
            char[] line = input[y].ToCharArray();
            for (int x = 0; x < line.Length; x++)
            {
                char c = line[x];
                if (c == '*')
                {
                    GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    cube.transform.position = new Vector3(x, y);
                    todelete.Add(cube);
                }
                if (c == '@')
                {
                    transform.position = new Vector3(x, y);
                }
            }
        }
        for (int angle=0; angle < 3600; angle++)
        {
            RaycastHit hit;
            Physics.Raycast(transform.position, Quaternion.Euler(0, 0, angle/10) * Vector3.up, out hit);
            if (hit.collider!=null)
            {
                GameObject hitObject = hit.collider.gameObject;
                if (!check.Contains(hitObject)&&hitObject!=this)
                {
                    total += 1;
                    check.Add(hitObject);
                }
           }
        }
        return total;
    }
}

Я використав 3600 радіопередач, тому що він не відповідає 5-му тестовому випадку з нижчим. Це все ще може не вдатися до ще більших / точніших тестових випадків.

На жаль, здається, що збірки webgl та настільних комп'ютерів зламаються, тому все, що у мене є, - це вихідний код для тестування на github .


read: worse than JavaЦе на 383 байти коротше, ніж рішення Java!
користувач8397947

@dorukayhan Я маю на увазі, що більшість вбудованих є більш багатослівними, ніж Java
Blue

Я не знаю про C # , але не могли б ви замінити total+=1з total++? Я думаю, що ще одним способом збереження деяких символів є створення куба будівлі та встановлення його позиції в одному операторі. Ви, здається, не використовуєте cubeзмінну ніде.
Фрозн

@Frozn Я насправді цього не роблю в своєму коді для гольфу
Blue

Просто подивився на код і побачив, що ти там змінив підрахунок. Я завжди вважаю, що версія для гольфу - це лише версія пробілу довше, але це, очевидно, не так. Щодо другої частини: я думаю, що ви так і зробите. Це GameObject b=GameObject.CreatePrimitive(PrimitiveType.Cube);b.transform.position=new Vector3(x,y);. Я не знаю, чи можливо це на C #, але в Java можна GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position=new Vector3(x,y);замість цього написати .
Фрозн

3

Java 8 лямбда, 1506 1002 972 942 символів

Мені хотілося перемогти цей виклик, оскільки це дуже цікаво. Результат (не дуже вибагливий) можна побачити тут:

import java.util.*;f->{Set<double[]>B=new HashSet(),r,n;double a,M,m,P=Math.PI*2,z=.5;int x=0,y,v=0,i,j,c[],p,q,l=g.length;for(;x<l;x++)for(y=0;y<g[x].length;y++)if(g[x][y]>63)for(;;){c=new int[]{-1};M=2e31-1;for(i=0;i<l;i++)for(j=0;j<g[i].length;j++)if(g[i][j]==42)if((m=(p=x-i)*p+(q=y-j)*q)<M){M=m;c=new int[]{i,j};}if(c[0]<0)break;g[c[0]][c[1]]=0;double[]A={(a=Math.atan2((c[1]-=y)-z,(c[0]-=x)-z))<0?a+P:a,(a=Math.atan2(c[1]+z,c[0]-z))<0?a+P:a,(a=Math.atan2(c[1]+z,c[0]+z))<0?a+P:a,(a=Math.atan2(c[1]-z,c[0]+z))<0?a+P:a};r=new HashSet();M=-P;m=P;for(double d:A){M=d>M?d:M;m=d<m?d:m;}r.add(new double[]{m,M});for(double[]t:B){n=new HashSet();for(double[]h:r)for(double[]u:t[0]<h[0]?t[1]<h[0]?new double[][]{h}:t[1]<h[1]?new double[][]{{t[1],h[1]}}:new double[0][]:t[0]>h[1]?new double[][]{h}:t[1]>h[1]?new double[][]{{h[0],t[0]}}:new double[][]{{h[0],t[0]},{t[1],h[1]}})if(u[0]<u[1])n.add(u);r=n;}B.addAll(r);if(!r.isEmpty())v++;}return v;}

Звичайно, це також існує у версії, що не має волі:

import java.util.*;

public class AngleCheck {

    static int getViewableBuildingsC(char[][] grid) {

        Set<double[]> blocked = new HashSet(), ranges, newRanges;

        double angle, max, min, PI2 = Math.PI * 2, half = 0.5;

        int x = 0, y, viewable = 0, i, j, building[], dX, dY, length = grid.length;

        for (; x < length; x++) {
            for (y = 0; y < grid[x].length; y++) {
                if (grid[x][y] > 63) {
                    for (;;) {
                        building = new int[]{-1};
                        max = 2e31-1;
                        for (i = 0; i < length; i++) {
                            for (j = 0; j < grid[i].length; j++) {
                                if (grid[i][j] == 42) {
                                    if ((min = (dX = x - i) * dX + (dY = y - j) * dY) < max) {
                                        max = min;
                                        building = new int[]{i, j};
                                    }
                                }
                            }   
                        }

                        if (building[0] < 0)
                            break;

                        grid[building[0]][building[1]] = 0;
                        double[] angles = {
                                        (angle = Math.atan2((building[1] -= y) - half, (building[0] -= x) - half)) < 0 ? angle + PI2 : angle,
                                        (angle = Math.atan2(building[1] + half, building[0] - half)) < 0 ? angle + PI2 : angle,
                                        (angle = Math.atan2(building[1] + half, building[0] + half)) < 0 ? angle + PI2 : angle,
                                        (angle = Math.atan2(building[1] - half, building[0] + half)) < 0 ? angle + PI2 : angle};

                        ranges = new HashSet();

                        max = -PI2;
                        min = PI2;
                        for (double d : angles) {
                            max = d > max ? d : max;
                            min = d < min ? d : min;
                        }

                        ranges.add(new double[]{min, max});

                        for (double[] reference : blocked) {
                            newRanges = new HashSet();
                            for (double[] currentRange : ranges) {
                                for (double[] subRange : reference[0] < currentRange[0] ?
                                            reference[1] < currentRange[0] ?
                                                // whole range after referencerange
                                                new double[][]{currentRange}
                                            :
                                                reference[1] < currentRange[1] ?
                                                    // lower bound inside referencerange, but upper bound outside
                                                    new double[][]{{reference[1], currentRange[1]}}
                                                :
                                                    // whole range inside referencerange -> nothing free
                                                    new double[0][]
                                        :
                                            // greater or equal lower bound
                                            reference[0] > currentRange[1] ?
                                                // whole range before referencerange
                                                new double[][]{currentRange}
                                            :
                                                // ranges overlap
                                                reference[1] > currentRange[1] ?
                                                    // range starts before and ends in reference range
                                                    new double[][]{{currentRange[0], reference[0]}}
                                                :
                                                    // referencerange is in the range -> two free parts, one before, one after this
                                                    new double[][]{{currentRange[0], reference[0]}, {reference[1], currentRange[1]}}) {
                                    if (subRange[0] < subRange[1])
                                        newRanges.add(subRange);
                                }
                            }
                            ranges = newRanges;
                        }

                        blocked.addAll(ranges);
                        if (!ranges.isEmpty()) {
                            viewable++;
                        }
                    }
                }
            }
        }
        return viewable;
    }
}

Це виглядає дуже складно, але це простіше, ніж можна подумати. Моя перша ідея полягала в тому, щоб використовувати якийсь алгоритм перетину, щоб перевірити, чи може бути проведена лінія від мого положення до будівлі без будь-яких перетинів. Для цього я вирішив скористатися алгоритмом Коена-Сазерленда та намалювати лінії до всіх чотирьох кутів будівлі. Це працювало досить добре для перших тестів, але останнє не вдалося. Я незабаром з’ясував, що це випадок, коли ви можете бачити не кути, а частину краю. Тому я подумав про якийсь рентгенівський кастинг, як @Blue, це зробив. Я вирішив це завдання, оскільки не досяг певного прогресу. Тоді я побачив відповідь Блю і мені прийшла в голову наступна проста ідея: Кожен будівельний блок блокує певний кут, в якому нічого іншого не видно. Мені просто потрібно стежити за тим, що можна побачити і що вже приховано іншими будівлями. Це воно!

Алгоритм працює так: Він визначає будівлю з найменшою відстані до людини. Тоді ми уявляємо чотири лінії, проведені від людини до кутів будівлі. Дві з них мають надзвичайне значення: Мінімальний і максимальний кут, під яким можна побачити будівлю. Ми приймаємо їх як діапазон і порівнюємо їх з іншими будівлями, про які ми знаємо, що їх можна побачити (на початку жодна). Діапазони можуть перетинатися, включати один одного або взагалі не торкатися. Я порівнюю діапазони і отримаю нові діапазони будівлі, які не заховані видимими будівлями. Якщо після порівняння його з будівлями, що залишилися, залишилось щось, будівля також може бути видно. Залишився діапазон кутів до списку діапазонів для порівняння та розпочнемо з наступної будівлі з наступної більшої відстані.

Іноді діапазони можуть перетинатися таким чином, що я закінчую діапазон 0 градусів. Ці діапазони будуть відфільтровані, щоб помилково не додати будівлю, яку навіть не можна переглянути.

Сподіваюся, хтось зрозумів це пояснення :)

Я знаю, що цей код не дуже гольф, я зроблю це якнайшвидше.

Це було справді складне завдання. Ви думали, що знайшли рішення, яке працює, але натомість ви ще далеко. Я думаю, що це рішення працює досить добре. Це не дуже швидко, але принаймні це працює;) Дякую за цю головоломку!


Оновлення

Я знайшов час для того, щоб переграти все це в єдину функцію, яку таким чином можна перетворити на лямбда. Усі функції були викликані лише один раз, і тому їх можна об'єднати в один метод. Я перейшов зі списків на набори, оскільки це економить додаткові символи. Декларації були складені разом. Порівняння були зібрані і символи були замінені на значення ascii. Порівнюючи діапазон можна виразити як багато тернарів. Деякі хитрощі тут і там, щоб запобігти довгим виразам, як Double.NEGATIVE_INFINITY було зроблено. По можливості проводяться рядкові призначення. Щоб заощадити трохи більше, я перейшов від порівняння кутів у градусах до порівняння радіанів. Ціла зміна врятувала понад 500 символів, я сподіваюся, що все це буде менше 1000;

Я видалив дженерики, де це можливо, і скоротив порівняльний показник, створивши масив одного елемента і перевірив його значення. Я також замінив Double.NEGATIVE_INFINITY на PI2 та -PI2, оскільки це верхня та нижня межі кутів. Тепер нарешті менше 1000 символів!

Я об'єднав петлі для пошуку місця розташування осіб та ітератора будівлі, щоб зберегти деякі символи. На жаль, це вимагає, щоб ми повернули вихід із циклу і все-таки використали перерву, але цього разу без мітки. Я злився maxі distanceSquaredта minі newDistanceSquaredяк вони не потрібні , в той же час. Я змінився Integer.MAX_VALUEна 2e31-1. Також я створив константу, half = 0.5яка використовується для обчислення кутів будівлі. Це коротше у гольф-версії. Загалом ми зберегли ще 30 символів!


Гарний гольф! Я взяв простіший маршрут із усіма вбудованими радіомовленнями, але приємно знати, що я допоміг! (BTW я, мабуть, також зміняться на набори)
Blue
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.