Co nowego na forum?

Refleksja po latach Kariera 2018-08-12 21:47

Asia dodał post w Rynek pracy IT w Poznaniu

1 minuta temu Zimny Żubr napisał(a): Czy zna ktoś firmy, które są zainteresowane współpracą ze stu...

axelbest dodał post w Jakie parametry na domowy serwer Git, Wiki, DLNA, dane

7 minut temu Najpierw powinienes powiedzieć jakim budżetem dysponujesz, bo można kupić nasa za 80...

tdudzik dodał komentarz w Truly zaawansowane Java Interview Questions

16 minut temu Ale to raczej problem z designem a nie memory leak

siloam dodał post w Pytania rekrutacyjne dla seniora

28 minut temu scibi92 napisał(a): @Aventus: a to rozumiem że jak aplikaacja jest "typowo biznesowa...

Darck dodał post w Czy warto iść w Xamarin i Azure

31 minut temu Co chwila powstają nowe frameworki multiplatformowe, mobilne. Jest w tym jeszcze mni...

jarekr000000 dodał komentarz w Pytania rekrutacyjne dla seniora

dziś, 06:58 @somekind: w tym miejscu załamałem się Tobą. Cykle w grafie obiektów to zupełna norm...

Bogaty Ogrodnik dodał post w Pytania rekrutacyjne dla seniora

dziś, 03:56 scibi92 napisał(a): @Aventus: a to rozumiem że jak aplikaacja jest "typowo biznesowa...

Freja Draco dodał post w Jak piszecie |?

dziś, 03:19 Świetny Kura napisał(a): Zacząłem uczyć się c++ i głowię się jak wygodnie pisać oper...

Freja Draco dodał post w Problem z WebAudio

dziś, 03:12 Wiesz, jak coś mi nie działa, to zwykle robię taki myk, że wpisuję w Google komunika...

Freja Draco dodał post w Nowa strona - nisko w wynikach wyszukiwania - prośba o rady

dziś, 03:05 A dlaczego nie możesz po prostu postawić tej nowej strony pod starym adresem? Potem...

furious programming dodał post w Przekreślenie w komentarzach

dziś, 03:01 Nie ma takiego opisu (póki co), który zawierałby wykaz wszystkich możliwych znacznik...

Freja Draco dodał post w Menu schowane pod stopką - jak to naprawić?

dziś, 02:55 Jeśli w danej kategorii nie mam wcale żadnych produktów wtedy nic nie zostanie zwróc...

kulson dodał post w Przekreślenie w komentarzach

dziś, 01:55 A gdzie jest opis gdzie jakich znaczników można użyć?

Freja Draco dodał post w Wygasające ciastka

dziś, 01:15 Jeśli plik HTML z testerem ciastek odpalasz po prostu z jakiegoś katalogu na dysku,...

aurel dodał komentarz w Projekt który mógłbym zamieścić w CV - czy koniecznie korzystać...

dziś, 01:09 klikanie na ślepo po Visualowym GUI, na zasadzie "a może jak kliknę tu to zadziała"....

stivens dodał komentarz w Jak odpalic aplikacje na VPS?

dziś, 01:06 a no to z tym index.html walnalem glupote

placeg dodał komentarz w Jak odpalic aplikacje na VPS?

dziś, 01:02 chown

somekind dodał komentarz w Projekt który mógłbym zamieścić w CV - czy koniecznie korzystać...

dziś, 01:00 A starczyło odpalić konsolę w katalogu projektu i wpisać git init.

stivens dodał komentarz w Jak odpalic aplikacje na VPS?

dziś, 00:59 co pomoglo chown czy dodatkowa sciezka?

Popularne wpisy na mikroblogu

4programmers.net
2018-07-03 20:02Sponsorowane

Pamiętacie Artura? Młody przedsiębiorca - freelancer IT, który wyszedł z problemów finansowych dzięki inviPay.com. W kwietniu otrzymał propozycję pracy nad projektem mechanizmu podzielonej płatności w jednym z banków działających w Polsce. Artur zgodził się na kontrakt, w którym miał zapewnione wysokie wynagrodzenie, ale dopiero 60 dni po ukończeniu i przetestowaniu prac. Artur stwierdził "Maćkowi i Konradowi zapłacę własnymi środkami, a po ukończeniu prac otrzymam swoje pieniądze". Wszyscy byli zachwyceni możliwością pracy nad wdrożeniem innowacyjnego rozwiązania jakim jest split payment.

Niestety to, co zawodowo okazało się interesującym zadaniem, biznesowo przyniosło masę problemów. Artur po oddaniu prac, wystawieniu faktury i odczekaniu 60 dni był zaskoczony, że przelew do niego nie dotarł. Zdziwiony zadzwonił do banku i załamany usłyszał: "Panie Arturze, od 1 lipca płatności wykonujemy split paymentem, a Pan posiada tylko rachunek osobisty". Artur w związku z intensywnością prac całkowicie zapomniał o zmianie swojego rachunku osobistego na firmowy. Co prawda, będąc przedsiębiorcą prowadzącym jednoosobową działalność gospodarczą, nie miał obowiązku posiadania rachunku firmowego, ale niestety to płatnik decydował o formie rozliczenia. Bank chcąc działać według nowych regulacji zrealizował płatność nowym, podzielonym mechanizmem i przelew został odrzucony.

Załamany Artur ponownie stracił płynność finansową. Na domiar złego przekroczył termin zapłaty kwartalnych podatków i fiskus zaczął się o nie upominać.

Tym razem Artur wiedział jak szybko zareagować. Udał się do banku, założył rachunek firmowy i w porozumieniu z bankiem wystawił nową fakturę. Niestety z nowym terminem płatności 60 dni. Dzień po wystawieniu Artur zadzwonił do Pani Magdaleny z inviPay.com, żeby pomogła mu odzyskać kontrolę nad swoimi finansami. Pani Magdalena wyjaśniła ze szczegółami wszystkie elementy mechanizmu podzielonej płatności. Artur następnego dnia otrzymał środki na konto za pomocą split payment, dzięki czemu mógł od razu przelać zaległe podatki do urzędu skarbowego i odzyskał płynność finansową.

A czy Ty wiesz co to split payment? Zadzwoń pod 22 295 15 05 - odpowiemy na wszystkie Twoje pytania.

Modliszka

@endrius: to może odezwij się do nich żeby Cię usunęli z bazy i po kłopocie? Przy obecnym prawie większość firm jest o wiele bardziej restrykcyjna i ostrożna

endrius

@Modliszka: kliknąłem magiczny odnośnik "Nie subskrybuj", żeby wypisać się z czegoś do czego się nigdy nie zapisywałem :). To handlowcy takim spamowaniem i nękaniem limity chcą wyrobić

kmph
2018-08-13 20:04

Walki z projektem ciąg dalszy. Chyba nie zdążę przed końcem wakacji zrobić wszystkiego, co planuję :(

Mam dość poważne problemy z duplikacją kodu. Niestety, choć staram się to ograniczać (o ile tylko owo ograniczanie nie zatrzyma mnie zbyt mocno), copypasty jest tam nadal zdecydowanie za dużo. Nie wszędzie mam rozsądne pomysły, jak się tego pozbyć.

Weźmy na przykład public class Block : IAction. Mój oryginalny pomysł był taki, że utrzymywanie bloka (a) kosztuje niewielką ilość Staminy na turę, jeśli Staminy nie ma to blok jest zdejmowany; (b) natychmiast, gdy przeciwnik ukończy swój ruch, blok jest zdejmowany. Interfejs IAction wymaga metody execute - świetnie zatem, niech Block implementuje execute w ten sposób, że zabiera nieco Staminy, sprawdza, czy przeciwnik jest wolny i jeśli tak, zdejmuje się.

Działa. Ale! Oprócz Block mam jeszcze i Dodge, które działa bardzo podobnie, tyle że zamiast blokowania ataku przeciwnika (pewne zmniejszenie obrażeń o % wynikły ze stosunku Power walczących) usiłuje wykonać unik (przyjęcie albo 0% albo 100% obrażeń w zależności od RNG, szansa zależy od stosunku Speed obu walczących). No i obie klasy duplikują kod odpowiedzialny za zdejmowanie się wskutek braku Staminy albo wskutek faktu, że przeciwnik zakończył już swój ruch.

Co gorsza: Za chwilę okazuje się, że jednak Block oraz sposób jego nakładania i zdejmowania to są dwie różne rzeczy. No bo teraz np. chcę zrobić taki ruch jak Ram. Działa on tak: user podbiega do przeciwnika, blokując podczas podbiegania, następnie atakuje i zdejmuje bloka. No i mam problem. Jak to zrobić bez duplikacji kodu? Duplikowanie całej klasy Block oprócz kodu odpowiedzialnego za zdejmowanie to już przesada, nawet dla mnie.

Chcę wielodziedziczenia! Wtedy, myślę, dałbym sobie klasę Block, klasę Dodge, klasę RequireStaminaPerTurn, klasę UntilEnemyFinishesMove, klasę UntilEndOfChannel. I teraz niech NormalBlock dziedziczy po Block, RequreStaminaPerTurn, UntilEnemyFinishesMove. I niech NormalDodge dziedziczy po Dodge, RequireStaminaPerTurn, UntilEnemyFinishesMove. I wreszcie niech ChannelBlock dziedziczy po Block, RequireStaminaPerTurn, UntilEndOfChanel. Wszystko. Teraz nawet można (teoretycznie, nie zrobiłem tego) dać postać, która będzie miała taki passive, że będzie mieć bez przerwy założonego bloka bez żadnych kosztów Staminy.

Niestety! Wielodziedziczenie jest be i nie ma go w C#. Coś jednak muszę wymyślić! Z jednej strony nie chcę duplikować kodu ponad miarę, a z drugiej strony, nauczony tym doświadczeniem, chcę wprowadzić i utrzymać zasadę, że żaden efekt mający wpływ na grę nie odpowiada za swoje własne zdejmowanie i nakładanie.

W końcu wymyśliłem. Block nie implementuje już IAction. Zamiast tego pojawiły się takie klasy, jak: public class PlaceEffect: IAction, public class RemoveEffect : IAction, taki interfejs: public interface RequireSetup : IEffect { List<IAction> onPlacement { get; } List<IAction> onRemoval { get; } List<IAction> onFailure { get; } } itepe, itede.

Biorąc pod uwagę fakt, że dodatkowo jeszcze, celem umożliwienia, by tury wykonywały się jednocześnie (w sensie, obaj gracze dają polecenia i wtedy wykonuje się tura, nie ma znaczenia który gracz wykonuje swą turę jako pierwszy) wprowadziłem wymóg, by każda IAction implementowała property o nazwie priority - no zrobiło mi się spaghetti. Te wszystkie priority, getPriority, PlaceEffect, onBlahblah => { new List<IAction> { new PlaceEffect(_priority: yaddayadda, _effect: new JakiśtamEfekt(_onFailure : new List<IAction> {new RemoveEffect(plepleple.... nie chce mi się kończyć otwartych nawiasów i tym podobne... Wydaje mi się że brzydkie, obawiam się że błędogenne, i co gorsza podobne ruchy powtarzają ten niemiły kod czyli i tak mamy copypasty.

No ale idziemy dalej. Teraz implementuję sobie damage over time. Na pierwszy ogień idzie Hurricane i Conflagration. Te efekty odnoszą się do całego Field, toteż atakują obu graczy. No i tu już mamy problem, gdyż to ma działać jako efekt "stackowalny", tj: wykonanie ruchu Hurricane nakłada stack efektu Hurricane który to stack zostanie zdjęty po czasie zależnym od statystyk gracza nakładającego ten stack. Tak samo Conflagration. Co więcej, Hurricane i Conflagration razem tworzą kombo FieryTornado. I wreszcie, Hurricane, oprócz damage over time, nakłada jeszcze na obu graczy taki debuff, że każdy ich ruch kosztuje nieco więcej Staminy.

Zaimplementowane. Mam klasę Weather : IExecutable<Field>, IOnTire. Implementowanie IExecutable<Field> jest po to, by mogła nakładać obrażenia każdej tury. Implementowanie IOnTire jest po to, by mogła zwiększać zużycie Staminy. W niej klasy: PlaceWeatherStack : PlaceEffect, RemoveWeatherStack : RemoveEffect (ich zadanie: nakładanie / zdejmowanie instancji klasy Weather do zbioru efektów na Field kiedy tylko zostanie nałożony jakiś stack Hurricane lub Conflagration / kiedy ostatni stack zostanie zdjęty); no i oczywiście klasy HurricaneStack, ConflagrationStack. Klasa Weather, wywołana, sumuje sobie wszystkie stacki i na tej podstawie zadaje obrażenia / zwiększa użycie Staminy. Powinno działać. (Jeszcze nie przetestowałem).

Niestety. Klasa Weather, ze wszystkimi klasami zagnieżdżonymi, to jest sporo kodu. Więcej, niżbym przewidywał. 200 linii z hakiem.

A teraz nadchodzą kolejne dwa efekty: ToxicSpores i BurningSpores. Także zadają damage over time, ale już tylko na przeciwników. Nie mają także dodatkowych efektów takich jak IOnTire. Mimo to... duplikacja kodu pomiędzy tymi klasami a Weather jest straszna. Nieakceptowalna nawet dla mnie.

Ale przecież nie chcę! zrobić klasy bazowej, choćby abstrakcyjnej, która by zawierała w sobie zduplikowany kod! Przecież nie ma wielodziedziczenia, a pojedyncza klasa bazowa to przepis na powrót problemów, na jakie natknąłem się przy Block. W końcu (chyba) udało mi się wydzielić część zarządzania stackami:

public class PlaceStackedEffect : PlaceEffect
    {
        public interface IStackedEffect : IEffect
        {
            List<IStackManager> managers { get; }
 
            void prepare(Species affected);
 
            IStackedEffect accumulate(IStackedEffect eff, IStackedEffect acc);
        }
 
        public interface IStackManager : IEffect
        {
            void update(IEffectStorage storage);
        }
 
        public IStackedEffect eff;
 
        public PlaceStackedEffect(IStackedEffect _eff, List<IEffect> _others, List<int> _priority = null, IEffectStorage _target = null) : base(_others.Prepend(_eff), _priority, _target)
        {
            eff = _eff;
        }
 
        public override void execute(Species affected)
        {
            foreach (IStackManager required in eff.managers)
                if (!target.ownEffects.OfType<IStackManager>().Any(eff => eff.GetType().IsAssignableFrom(required.GetType())))
                    target.ownEffects.Add(required);
 
            eff.prepare(affected);
            base.execute(affected);
 
            foreach (IStackManager manager in target.ownEffects.OfType<IStackManager>().Where(sm => eff.managers.Any(required => sm.GetType().IsAssignableFrom(required.GetType()))))
                manager.update(affected);
        }
 
        public class RemoveStackedEffect : RemoveEffect
        {
            IStackedEffect eff;
 
            public RemoveStackedEffect(IStackedEffect _eff, List<IAction> _onRemove = null, List<int> _priority = null, IEffectStorage _target = null) : base(_eff, _onRemove, _priority, _target)
            {
                eff = _eff;
            }
 
            public override void execute(IEffectStorage affected)
            {
                base.execute(affected);
 
                foreach (IStackManager required in eff.managers)
                    foreach (IStackManager manager in target.ownEffects.Where(eff => eff.GetType().IsAssignableFrom(required.GetType())))
                        manager.update(affected);
            }
        }
    }

Wciąż nie usuwa to znacznej części duplikacji... np. sumowanie damage jest analogiczne we wszystkich 3 przypadkach, ale tego nie chcę wydzielać do IStackedEffect, bo łatwo wyobrazić sobie taki efekt, który by się zupełnie inaczej agregował. Tak samo analogiczny jest kod odpowiadający za dispelowanie stacków, ale znowu: boję się powrotu problemów spod znaku Block (tj. duplikacja kodu wymuszona brakiem wielodziedziczenia). No bo co, jeśli jakiś inny efekt stackowalny będzie inaczej dispelował stacki i co więcej, tak jak w przypadku Block / Dodge, jego inne dispelowanie też winno być dziedziczone po iluśtam innych efektach?

Przyznaję, że ten projekcik jest dla mnie solidną lekcją pokory.

kmph

@Afish: Dzięki za porady. Spróbuję przerobić projekt zgodnie z Twoimi sugestiami. Jednak zajmę się tym raczej później: postawiłem sobie (nie wiem, czy słusznie), że wielkich zmian architektonicznych nie będę robił, zanim nie będę miał działającej (choćby zabugowanej, ale działającej) wersji alfa. A to dlatego, że chciałbym mieć tę alfę jak najszybciej, najchętniej przed końcem wakacji (choć to nierealne raczej :( ), a poza tym boję się paralysis by analysis: ciągłego przepisywania i poprawiania kodu tak, że nic nie zrobię. Z ruchów do zaprototypowania pozostały mi jeszcze 2, potem zajmę się innymi partiami projektu, i wtedy rzeczywiście mam w planach ogarnięcie całości i przepisanie jej. Twe sugestie z pewnością okażą się tu bardzo pomocne. Chyba, że radziłbyś mi raczej przepisać to natychmiast...?

Afish

Jak się zastanawiasz, czy przepisywać teraz, to nie przepisuj, dzięki temu kiedyś dojdziesz do momentu, w którym będziesz wiedział, kiedy to potencjalnie należało przepisać i czy ten moment przegapiłeś, a to też jest cenna wiedza.

Shizzer
2018-08-12 15:50

Dziś skończyłem pisać ostatni post na moim blogu na temat interpretera Brainfucka pisanego w C. Program ten skończyłem pisać już trochę czasu temu i już nawet zająłem się nowym projektem, ale z racji, że ciężko mi się oderwać od kodu to po prostu odkładałem pisanie tych artykułów. :) Od czasu mojego ostatniego wpisu tutaj napisałem jeszcze bodajże trzy posty. Jeśli jesteście zainteresowani jak poradziłem sobie z napisaniem tego interpretera od początku do końca to zapraszam - https://shizz3r.blogspot.com/

Dodam, że teraz tworzę usługę serwera FTP + klienta w oparciu o programowanie asynchroniczne w Pythonie. Postaram się opisać również i ten projekt także jeśli jesteście ciekawi to śledźcie bloga. :)

Oczywiście zaznaczam, że nie jestem ekspertem dlatego wszelka konstruktywna krytyka jest mile widziana. :)

msm

Tak prywatnie, to straszny overengineering w tym projekcie ;). Cały interpreter brainfucka można lajtowo zamknąc w 50-100 linijkach (zaleznie od zwięzłości stylu) a Ty już do 300 dochodzisz. Schludność kodu OK, ale jakość często biedna (np. te definy na górze - O_o).

Mikroblog to nie miejsce na takie wywody, ale jak napiszesz do działu Własne Projekty (albo na PM w ostateczności) to mogę rozwinąć co IMO jest słabo napisane.

kodokleta

@msm: czysty kod... tak odruchowo bym to tłumaczył... jest tam?... a nie... pliki powinny mieć mniej niż 100 linijek