Vinnaren i pepparkakshustävlingen!
2017-09-08, 20:49
  #13
Medlem
kakelpannas avatar
EDIT: const int kommer alltid optimeras precis som en constexpr om inte kompilatorn suger. Det enda sättet att faila där är ifall du använder "extern const int".

Citat:
Ursprungligen postat av RostigHink
... och den dagen du har ett projekt som växer så att du vill dela upp kod i headers och moduler får du gå in och byta till constexpr. Varför inte använda det från början?

Varför? För att du vill använda C++ och det vore väl bra om du använde språkets styrka. Även om man inte måste använda OOP i C++ så finns det fördelar i framförallt läsbarhet.

Men visst, för småprojekt som ryms på ett par skärmsidor är det strunt samma vilken kodningsstil man använder.

Vad jag ställer mig frågande till är vad du egentligen vill diskutera. Är det språket, kodningsmönster eller algoritmerna?

Kodningsmönster och algoritmer, möjligen språket också.

constexpr var en bra idé, även om det inte spelade någon roll i det här fallet. Jag har lärt mig att folk förväntar sig C++-idiom även om målet är att så snabbt som möjligt att slänga ihop något litet på det här viset. Det blir liksom svårt för folk att läsa det, om man säger så.

Även om andra mer erfarna hade använt sig av funktioner på det här viset (typ C-style) så hade de inte använt globaler, utan de hade för läsbarhetens skull låtit funktionerna ta en referens/argument så att "allt finns där" i en och samma funktion. Jag debatterade/disktuerade (elller som många uppfattar det, sade emot) först om att använda globaler istället, men mina argument höll inte. Det är också alldeles för enkelt att slänga dit en extra parameter och har många fördelar, du kan t.ex. placera argumenten som skickas (de som innnan var globalerna) i sjävla game-loopen. Testning, som jag inte tänkt på, underlättas då man kan testa funktionerna separat.

Sedan var det något om putsning, typ bättre namn och flytta runt saker. Det hade jag kunnat göra men orkade inte. Jag tappade mig på kommentererna också. T.ex. /* Draw block */ void drawblock();. Att flytta runt saker inser jag nu ändå inte hade gjort i närheten lika mycket som att ge funktionerna parametrar och helt enklet plocka bort globalerna.

Sedan finns det folk som inte dogmatiskt skulle använda sig av namespace och uppdelade filer här och håller med om att det är okej att slänga ihop allt i en enda fil om det är tillräckligt litet och man vet att det så ska förbli, för ibland vet man helt enkelt att "detta är det", t.ex. när man testar nya algoritmer för tetris och bara så snabbt som möjligt vill få det gjort där fancy överabstrakta funktioner inte tjänar något.
__________________
Senast redigerad av kakelpanna 2017-09-08 kl. 21:34.
Citera
2017-09-09, 09:15
  #14
Moderator
RostigHinks avatar
Jag minns inte om jag använt ncurses men en snabbtitt på det så inbjuder det inte till kodning i typiska C++-paradigmer om man inte wrappar hela ncurses vilket ser ut som ett hästjobb. ncurses i sig är däremot härdat för C++ så man behöver inte stänga in det i extern "C" {}.

Vad man kan göra är att endast rita om spelplanen när något händer så slipper man flimrandet. Jag testade med en bool redraw i ingame_loop() som initieras till false och sätts true om planen behöver ritas om. Anropet till drawboard() villkoras på redraw. Planens ram ritades dock inte men jag grävde inte vidare i varför.
Citera
2017-09-10, 02:03
  #15
Medlem
kakelpannas avatar
Citat:
Ursprungligen postat av RostigHink
Jag minns inte om jag använt ncurses men en snabbtitt på det så inbjuder det inte till kodning i typiska C++-paradigmer om man inte wrappar hela ncurses vilket ser ut som ett hästjobb. ncurses i sig är däremot härdat för C++ så man behöver inte stänga in det i extern "C" {}.

Vad man kan göra är att endast rita om spelplanen när något händer så slipper man flimrandet. Jag testade med en bool redraw i ingame_loop() som initieras till false och sätts true om planen behöver ritas om. Anropet till drawboard() villkoras på redraw. Planens ram ritades dock inte men jag grävde inte vidare i varför.

Stämmer. ncurses har en massa preprocessor-grejer för C++. Att ramen inte visades kan bero på att koden clearar fönstret någonstans. refresh() är det som ritar upp skärmen.

Intressant att du vet att det jab flimmra, körde du alltså koden? Om man kör 10ms instället för 1ms på timeout så tror jag flimret försvinner.

Det stämmer också att onödiga delar ritas om. Ncurses printar bara ut förändringarna man gjort i "backbuffern", men i den här koden clearar och skrivs ju allt om. Innan jag började projektet tänkte jag att jag skulle sudda ut och rita om det rörande blocket, men det kändes inte lönt.
Citera
2017-09-10, 07:43
  #16
Moderator
RostigHinks avatar
Citat:
Ursprungligen postat av kakelpanna
Stämmer. ncurses har en massa preprocessor-grejer för C++. Att ramen inte visades kan bero på att koden clearar fönstret någonstans. refresh() är det som ritar upp skärmen.

Intressant att du vet att det jab flimmra, körde du alltså koden? Om man kör 10ms instället för 1ms på timeout så tror jag flimret försvinner.

Det stämmer också att onödiga delar ritas om. Ncurses printar bara ut förändringarna man gjort i "backbuffern", men i den här koden clearar och skrivs ju allt om. Innan jag började projektet tänkte jag att jag skulle sudda ut och rita om det rörande blocket, men det kändes inte lönt.
Med även clear() villkorad blir det helt flimmerfritt och ramen syns:
Kod:
bool ingame_loop() {
  bool redraw = false;
  int c = getch();

  if (c == 'q' || c == 'Q')
    return false;

  if (lost) {
    mvprintw(0, 0, "You lost. Press q to quit.");
    refresh();
    return true;
  }

  bool down = false;

  if(--tick_force_down < 0) {
    tick_force_down = level;
    down = true;
    redraw = true;
  }

  switch (c) {
    case ' ':
      down = true;
      while (movedown());
      redraw = true;
      break;
    case 'z':
    case 'Z':
      rotleft();
      redraw = true;
      break;

    case 'x':
    case 'X':
    case KEY_UP:
      rotright();
      redraw = true;
      break;

    case KEY_LEFT:
      moveleft();
      redraw = true;
      break;

    case KEY_RIGHT:
      moveright();
      redraw = true;
      break;

    case KEY_DOWN:
      down = true;
      redraw = true;
      break;
  }

  if (down) {
    tick_force_down = level;
    if (!movedown()) {
      raster();
      if (!drop()) {
        lost = true;
      }
      else {
        int lines = clearlines();
        level -= lines;
        score += lines;
      }
    }
  }

  // Update the screen
  if (redraw) {
    clear();
    drawboard();
  }
  drawmoving();
  drawnext();
  mvprintw(score_y, score_x, "Score: %d", score);
  refresh();

  return true;
}

Visst körde jag koden, inget konstigt då den kompilerar utan problem, i detta fall med gcc. Du får ursäkta formateringen, gammal vana från förra jobbets kodningsregler men samtidigt en hint om hur jag och en massa kollegor i programmerarskrået vill ha det. Det är så jag tänkte med att bara uppdatera vid ett event. Nästa steg kan vara att låta processen sova tills det händer något genom att ett block ska flyttas ner eller tangentbordstryck. Allt för att spara ström

Kanske att ta hand om ofrivillig terminering som ^C i en linuxterminal då den lämnar terminalen i grafikläge.
Citera
2017-09-10, 08:27
  #17
Avstängd
Citat:
Ursprungligen postat av kakelpanna
Intressant att du vet att det jab flimmra, körde du alltså koden? Om man kör 10ms instället för 1ms på timeout så tror jag flimret försvinner.

Det stämmer också att onödiga delar ritas om. Ncurses printar bara ut förändringarna man gjort i "backbuffern", men i den här koden clearar och skrivs ju allt om. Innan jag började projektet tänkte jag att jag skulle sudda ut och rita om det rörande blocket, men det kändes inte lönt.

Att skriva till en cell tar ingen tid alls eftersom det bara påverkar den interna skärmbuffern. Om man tvingar systemet att rita om hela skärmen p.g.a. några ändrade pixels, måste man vara sjukt lat.
Citera
2017-09-11, 21:48
  #18
Medlem
kakelpannas avatar
Citat:
Ursprungligen postat av SuperSizeMe
Att skriva till en cell tar ingen tid alls eftersom det bara påverkar den interna skärmbuffern. Om man tvingar systemet att rita om hela skärmen p.g.a. några ändrade pixels, måste man vara sjukt lat.

Inte lat. Det gav snabbare de resultat jag ville ha. Vill jag ha dit något nytt så behöver jag inte skriva en ny funktion för det (t.ex. att selektivt rita om "next block"), utan jag kan bara ändra next_block så är det klart och clearas/ritas automatiskt.
Citera
2017-09-11, 22:40
  #19
Avstängd
Citat:
Ursprungligen postat av kakelpanna
Inte lat. Det gav snabbare de resultat jag ville ha. Vill jag ha dit något nytt så behöver jag inte skriva en ny funktion för det (t.ex. att selektivt rita om "next block"), utan jag kan bara ändra next_block så är det klart och clearas/ritas automatiskt.

Använd isf erase() istället för clear(). Den raderar bara det interna.

https://lists.gnu.org/archive/html/b.../msg00007.html

Men det tar tid att nollställa hela skärmen även internt. Bitar borde vara objekt med metoder för att rita och "av-rita".

Du behöver inte uppdatera varje millisekund eftersom det mänskliga ögat bara reagerar typ 25 ggr i sekunden. Använd nodelay() istället för timeout(), sen i loopen sover du 40 ms med napms(), tömmer teckenbuffern med upprepade anrop till getch() samtidigt som du ändrar den interna skärmen efter vad som trycktes, och avslutar med refresh() om det behövs.
__________________
Senast redigerad av SuperSizeMe 2017-09-11 kl. 23:31.
Citera
2017-09-12, 03:10
  #20
Medlem
kakelpannas avatar
Citat:
Ursprungligen postat av SuperSizeMe
Använd isf erase() istället för clear(). Den raderar bara det interna.

https://lists.gnu.org/archive/html/b.../msg00007.html

Men det tar tid att nollställa hela skärmen även internt. Bitar borde vara objekt med metoder för att rita och "av-rita".

Du behöver inte uppdatera varje millisekund eftersom det mänskliga ögat bara reagerar typ 25 ggr i sekunden. Använd nodelay() istället för timeout(), sen i loopen sover du 40 ms med napms(), tömmer teckenbuffern med upprepade anrop till getch() samtidigt som du ändrar den interna skärmen efter vad som trycktes, och avslutar med refresh() om det behövs.

Varför inte bara timeout 40 ms?
Citera
2017-09-12, 07:12
  #21
Avstängd
Citat:
Ursprungligen postat av kakelpanna
Varför inte bara timeout 40 ms?

Det låter användaren påverka timingen. Om getch() returnerar en tangent, vet du inte om den trycktes innan anropet eller om getch() fick sova och isf hur länge.
Citera
2018-05-19, 21:04
  #22
Medlem
Något som är bra programming practice är att lägga all kod du har i main och lägga det i en ny funktion utanför, ex: void startGame(); I detta fall spelar det ingen roll men det gör skillnad i längden skulle jag säga. Då kan du ha en annan funktion som heter openMenu(); och kanske showHighscores(); osv

En bra tumregel är att nybörjare ska kunna avgöra vad som sker i main.
Citera
2018-05-19, 22:09
  #23
Medlem
Brunbeverns avatar
Citat:
Ursprungligen postat av kakelpanna

Sedan finns det folk som inte dogmatiskt skulle använda sig av namespace och uppdelade filer här och håller med om att det är okej att slänga ihop allt i en enda fil om det är tillräckligt litet och man vet att det så ska förbli, för ibland vet man helt enkelt att "detta är det", t.ex. när man testar nya algoritmer för tetris och bara så snabbt som möjligt vill få det gjort där fancy överabstrakta funktioner inte tjänar något.

Fast med den premissen borde du välja något annat språk, Python kanske? Är ju ingen vits att välja C++ av alla språk, för att sedan använda argumentet att du försöker slänga ihop det så fort som möjligt för att prova och inte bry dig om hur det är upplagt.
Citera
2018-05-19, 22:53
  #24
Medlem
Citat:
Ursprungligen postat av klosor
Något som är bra programming practice är att lägga all kod du har i main och lägga det i en ny funktion utanför, ex: void startGame(); I detta fall spelar det ingen roll men det gör skillnad i längden skulle jag säga. Då kan du ha en annan funktion som heter openMenu(); och kanske showHighscores(); osv

En bra tumregel är att nybörjare ska kunna avgöra vad som sker i main.

Spinner vidare på det

Jag har läst ungefär noll om strukturering av program, men rent logiskt och förenklat tänker jag såhär: om man bryter ut och skapar en funktion utan att (planera att) anropa den fler än en gång, gör man det för läsbarheten och överskådlighetens skull. Och det skulle man kunna göra bättre med kommentarer och/eller någon texteditor-funktion som inte har uppfunnits än, än en sammanfattning i ett funktionsnamn.

Om man delar upp kod borde det vara viktigare att bli av med så många beroenden som möjligt mellan delarna, än att döpa funktionerna kort och enkelt. startGame(), openMenu() och showHighscores() är trivala exempel som hyfsat uppfyller båda, men ändå: Vilken av dem kan anropas medan vilken annan körs? Vilken av dem rensar hela skärmen? Vilken av dem har en liten plats i nedre högra hörnet, och när?

Vilken har högst prioritet i ljudkön? Högst processorprioritet? Osv... Att kämpa för att beskriva allt det här m.h.a. en sekventiell rad av tecken i en källkodsfil, är stenålders och lever kvar som en romantisk dröm. Vi har full 2d nu, och snart 3d vilket öppnar ett helt nytt djup.
Citera

Stöd Flashback

Flashback finansieras genom donationer från våra medlemmar och besökare. Det är med hjälp av dig vi kan fortsätta erbjuda en fri samhällsdebatt. Tack för ditt stöd!

Stöd Flashback