سؤال

موافق لذلك هناك الكثير من "كونواي لعبة الحياة" الأسئلة ولكن هذا هو واحد جميلة محددة.أنا ذاهب إلى رمي حفنة من التعليمات البرمجية في الأولى, كسر عليه و تظهر لك أين المشكلة.

حتى هنا هو بلدي لعبة كونواي الحياة التنفيذ حتى الآن ، الآن هو محدودة إلى وحدة التحكم من أجل التصحيح (JSfiddle - http://jsfiddle.net/georeith/C9Gyr/8/ - اطلاق النار عليه ، فتح وحدة التحكم الخاصة بك):

var utils = {};

/*
 * utils.extend()
 * - Extend initial object with all properties of following objects, objects later in the argument list take precedence.
 */
utils.extend = function(obj) {
  var args = Array.prototype.slice.call(arguments, 1);
  for (var i = args.length; i--;) {
    for (var prop in args[i]) {
      obj[prop] = args[i][prop];
    }
  }
  return obj;
}

/*
 * utils.defaults()
 * - Overwrite initial object with properties of following objects only if key is present in the initial object.
 */
utils.defaults = function(obj) {
  var args = Array.prototype.slice.call(arguments, 1);
  for (var i = args.length; i--;) {
    for (var prop in args[i]) {
      if (obj.hasOwnProperty(prop)) {
        obj[prop] = args[i][prop];
      }
    }
  }
  return obj;
}

/* no-wrap positioning functions */
var calcPos = {
  ul: function(cell) {
    return [cell.x - 1, cell.y - 1];
  },
  um: function(cell) {
    return [cell.x, cell.y - 1];
  },
  ur: function(cell) {
    return [cell.x + 1, cell.y - 1];
  },
  l: function(cell) {
    return [cell.x - 1, cell.y];
  },
  r: function(cell) {
    return [cell.x + 1, cell.y];
  },
  ll: function(cell) {
    return [cell.x - 1, cell.y + 1];
  },
  lm: function(cell) {
    return [cell.x, cell.y + 1];
  },
  lr: function(cell) {
    return [cell.x + 1, cell.y + 1];
  }
}

var worldDefaults = {
  rows: 50,
  columns: 50,
  wrap: true, // left edge is mirrored on right, top edge is mirrored on bottom. Vice versa
  speed: -1, // milliseconds (minimum time, waits until end of last tick to calculate from)
  grid: []
}
var World = function (opts) {
  this.settings = utils.defaults(worldDefaults, opts);

  this.maxX = this.settings.columns - 1;
  this.maxY = this.settings.rows -1;
  for (var y = 0, yLen = this.settings.rows; y < yLen; ++y) {
    for (var x = 0, xLen = this.settings.columns; x < xLen; ++x) { 
      if (y === 0) {
        this.cellList.push([]);
        if (this.settings.grid.length <= x) {
          this.settings.grid.push([]);
        }
      }
      var cell = new Cell();
      cell.x = x;
      cell.y = y;
      cell.alive = !!this.settings.grid[x][y];

      if (cell.alive) {
        this.lifeList.push(cell);
      }

      var lx = (x) ? x - 1 : this.maxX;
      var uy = (y) ? y - 1 : this.maxY;
      var ux = (x == this.maxX) ? 0 : x + 1;
      var ly = (y == this.maxY) ? 0 : y + 1;

      cell.neighbourCoords = (this.settings.wrap) ?
      [
        [lx, uy],   [x, uy],  [ux, uy],
        [lx,  y], /*[x,  y]*/ [ux,  y],
        [lx, ly],   [x, ly],  [ux, ly]
      ]
      :
      [
        calcPos.ul, calcPos.um, calcPos.ur,
        calcPos.l, calcPos.r,
        calcPos.ll, calcPos.lm, calcPos.lr
      ]
      ;
      this.cellList[x][y] = cell;
    }
  }
}
World.prototype.generation = 0;
World.prototype.cellList = [];
World.prototype.lifeList = [];
World.prototype.changeList = [];
World.prototype.nextTick = null;

/* Progresses the world */
World.prototype.tick = function() {
  var newLifeList = [];
  this.changeList = [];

  // This hash goes out of scope after each tick allowing any dead shadowCells to be garbage collected
  if (!this.settings.wrap) {
    var shadowCellHash = {};
  }

  for (var i = 0, iLen = this.lifeList.length; i < iLen; ++i) {
    var cell = this.lifeList[i];
    if (cell.key) {
      shadowCellHash[cell.key] = cell;
    }
    cell.neighbours = 0;
    cell.lastIterated = this.generation;

    for (var j = 0, jLen = cell.neighbourCoords.length; j < jLen; ++j) {

      var coords;
      var neighbour;
      if (this.settings.wrap) {
        coords = cell.neighbourCoords[j];
        neighbour = this.cellList[coords[0]][coords[1]];

      } else {
        coords = cell.neighbourCoords[j](cell);
        if (coords[0] > this.maxX || coords[0] < 0 || coords[1] > this.maxY || coords[1] < 0) {
          // This neighbour is off the screen so will require a shadowCell
          var key = ''+coords[0]+','+coords[1];
          if (!shadowCellHash[key]) {
            neighbour = shadowCellHash[key] = new ShadowCell(coords[0], coords[1]);
            neighbour.neighbourCoords = cell.neighbourCoords;
          } else {
            neighbour = shadowCellHash[key];
          }
        } else {
          neighbour = this.cellList[coords[0]][coords[1]];
        }
      }


      if (neighbour.lastIterated !== this.generation) {
        neighbour.neighbours = 0;
        neighbour.lastIterated = this.generation;
      }
      if (neighbour.alive !== neighbour.changed) {
        // neighbour started as alive
        ++cell.neighbours;
      } else {
        // neighbour started as dead
        ++neighbour.neighbours;
        if (neighbour.neighbours === 3) {
          neighbour.alive = true;
          neighbour.changed = true;
          neighbour.changeIndex = this.changeList.push(neighbour) - 1;
        } else if (neighbour.neighbours === 4) {
          // neighbour has reverted to dead
          neighbour.alive = false;
          neighbour.changed = false;
          neighbour.changeIndex = -1;
          this.changeList[neighbour.changeIndex] = undefined;
        }
      }
    }
    if (cell.neighbours < 2 || cell.neighbours > 3) {
      cell.changed = true;
      cell.alive = false;
      cell.changeIndex = this.changeList.push(cell) - 1;
    } else {
      newLifeList.push(cell);
    }
  }

  for (var i = 0, iLen = this.changeList.length; i < iLen; ++i) {
    var cell = this.changeList[i];
    if (cell !== undefined) {
      cell.changeIndex = -1;
      if (cell.alive) {
        newLifeList.push(cell);
      }
      cell.update();
      cell.changed = false;
    }
  }

  this.lifeList = newLifeList;
  ++this.generation;
  this.onTick();

  var that = this;
  if (this.settings.speed >= 0) {
    this.nextTick = setTimeout(function() {
      that.tick();
    }, this.settings.speed);
  }
  return this;
}

World.prototype.out = function() {
  var s = '';
  for (var y = 0, yLen = this.settings.rows; y < yLen; ++y) {
    for (var x = 0, xLen = this.settings.columns; x < xLen; ++x) {
      s += (this.cellList[x][y].alive)? '\u2B1B' : '\u2B1C';
    }
    s += '\n';
  }
  s += '\u21B3 Generation: ' + this.generation + ' -- Cells: ' + this.lifeList.length + ' \u21B5';
  s += '\n';
  return s;    
}

World.prototype.stop = function() {
  this.speed = -1;
}

World.prototype.onTick = function() {
  return this;
}

var Cell = function() {
  return this;
}
Cell.prototype.x = 0;
Cell.prototype.y = 0;
Cell.prototype.neighbours = 0;
Cell.prototype.alive = false;
Cell.prototype.changed = false;
Cell.prototype.changeIndex = -1;
Cell.prototype.lastIterated = -1;

/*
 * ShadowCell
 * - non rendered cell for use in no-wrap
 */
var ShadowCell = function(x,y) {
  this.x = x;
  this.y = y;
  this.key = ''+this.x+','+this.y;
  return this;
}
ShadowCell.prototype = utils.extend({}, Cell.prototype);
ShadowCell.prototype.isShadow = true;
ShadowCell.prototype.update = function(){
  return this;
};

/*
 * Cell.update()
 * - Update cell after tick
 */
Cell.prototype.update = function() {
  this.render();
  return this;
}

/*
 * Cell.render()
 * - Placeholder function to be overwritten by rendering engine
 */
Cell.prototype.render = function() {
  return this;
}

طريقة إخترت ينطوي على مجموعة من الخلايا التي هي على قيد الحياة في بداية كل جيل.ثم تكرار عبر كل من 8 الجيران تقرر ما إذا كنت تريد إنشاء/حذف لهم.

هذا يعمل كبيرة عندما تمر wrap: false إلى World منشئ (انظر JSfiddle للتنفيذ) ، هذا يقول أن المرآة الجانبين وعدم السماح تجاوز.غير أن هذا النمط من التخطيط يكسر الكثير من أنماط لأنه يسبب خلايا أن يعود على أنفسهم لذلك أنا أيضا أريد أن تسمح لها حساب خارج الشبكة.

لهذا الغرض تم إنشاء ShadowCell الدرجة التي تتصرف في الغالب نفس Cell فئة (كل خلية الشبكة حيا أو ميتا مثيل من ذلك) إلا أن ShadowClass هي فقط التي تم إنشاؤها عند غير موجودة الخلية المطلوبة خارج الشبكة و يتم تقديم لجمع القمامة اللحظة لم يعد مطلوبا (إذا كان ميتا بعد كل جيل).وإلا فإنه يحاكي Cell وفصول الصفات و يناسب مباشرة في نفس المنطق الذي Cell لا.

المسألة

إذا ذهبت إلى "الجيل 4" في وحدة الإخراج قد تلاحظ أنه ليس صحيحا تماما...

Hmmmmm

لقد ضاقت هذه المسألة وصولا الى ShadowCell التنفيذ لأن هذا يعمل إذا لم توفر ما يكفي من الحشو حول الشكل بحيث لا تجاوز الشبكة (وهو عندما ShadowCell في ركلات) ، على الرغم من أن كما قلت سابقا ShadowCell هو نسخة من Cell فئة لديها نفس السمات ويحصل مرت كما لو كانت Cell.

لأنني أريد أن تكون هذه القمامة التي تم جمعها لا تشمل هذه في الشبكة الشاملة مجموعة World.cellList...هذا يقودني إلى الاعتقاد المشكلة تكمن في هذا القسم من المدونة:

// This hash goes out of scope after each tick allowing any dead shadowCells to be garbage collected

if (!this.settings.wrap) {
  var shadowCellHash = {};
}

for (var i = 0, iLen = this.lifeList.length; i < iLen; ++i) {
  var cell = this.lifeList[i];
  if (cell.key) {
    shadowCellHash[cell.key] = cell;
  }
  cell.neighbours = 0;
  cell.lastIterated = this.generation;

  for (var j = 0, jLen = cell.neighbourCoords.length; j < jLen; ++j) {

    var coords;
    var neighbour;
    if (this.settings.wrap) {
      coords = cell.neighbourCoords[j];
      neighbour = this.cellList[coords[0]][coords[1]];

    } else {
      coords = cell.neighbourCoords[j](cell);
      if (coords[0] > this.maxX || coords[0] < 0 || coords[1] > this.maxY || coords[1] < 0) {
        // This neighbour is off the screen so will require a shadowCell
        var key = ''+coords[0]+','+coords[1];
        if (!shadowCellHash[key]) {
          // ShadowCell not in hash, let's create one
          neighbour = shadowCellHash[key] = new ShadowCell(coords[0], coords[1]);
          neighbour.neighbourCoords = cell.neighbourCoords; 
          // NOTE: neighbourCoords are a set of functions that return values relative to the cell you pass to them. I am not literally giving the `ShadowCell` the same neighbour positions here.
        } else {
          neighbour = shadowCellHash[key];
        }
      } else {
        // This neighbour is on screen, grab its cell.
        neighbour = this.cellList[coords[0]][coords[1]];
      }
    }
    ...

ملاحظة: على قيد الحياة ShadowCells لن يكون القمامة التي تم جمعها لأنها تحصل المخزنة في صفيف مع خلايا أخرى (أنا متأكد من هذا من التصحيح ، انظر عدد الخلايا في وحدة الإخراج و عد الخلايا المرئية).

لسبب ما ShadowCell الطبقة يبدو أن يسبب التقارير غير صحيحة من الجيران.أنا حاولت تصحيح ذلك باتباع إنشاء, حذف و تحسب جيران كل خلية على حدة خلال كل جيل لكن عقلي يموت قبل أن نضع كل ذلك معا.كل ما عندي من التصحيح الجهود لا أستطيع أن أرى لماذا هذا السلوك يجب أن تحدث . ShadowCell هو الى حد كبير نفس ، Cell إلى كل شيء آخر أن يستخدم (أنها تستخدم نفس الموقف وظائف .الخ), حقيقة انها لا تحصل على المقدمة يجب أن لا تكون سببا في هذا.

عن الجيل 4 أحصل على الناتج التالي عن طريق تسجيل الدخول إنشاء الظل خرائط, أستطيع أن أرى أن كل يجري إنشاؤه مرة في كل جيل (ملاحظة:فئة لا تظهر لأنني كنت utils.extend() إلى إنشاء لقطة منهم):

Object {x: 5, y: -1, key: "5,-1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 6, y: -1, key: "6,-1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 7, y: -1, key: "7,-1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 4, y: -1, key: "4,-1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: -1, y: 1, key: "-1,1", neighbourCoords: Array[8], neighbours: 0…}
Object {x: -1, y: 2, key: "-1,2", neighbourCoords: Array[8], neighbours: 0…}
Object {x: -1, y: 3, key: "-1,3", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 5, y: -2, key: "5,-2", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 6, y: -2, key: "6,-2", neighbourCoords: Array[8], neighbours: 0…}
Object {x: 7, y: -2, key: "7,-2", neighbourCoords: Array[8], neighbours: 0…}
Object {x: -1, y: 4, key: "-1,4", neighbourCoords: Array[8], neighbours: 0…}

دخل على خط 152 مثل ذلك:

if (!shadowCellHash[key]) {
  neighbour = shadowCellHash[key] = new ShadowCell(coords[0], coords[1]);
  neighbour.neighbourCoords = cell.neighbourCoords;
  console.log(utils.extend({}, neighbour));
} else {
هل كانت مفيدة؟

المحلول

shadowCellHash لم يتم تهيئة مع كل من ShadowCells قبل البدء في تنفيذ الحلقات خلال كل خلية تبحث عن الجيران.عندما الحلقة الشيكات [5,-1] عن الجيران لا تجد [6,-1] لأنه ليس في shadowCellHash.منذ [6,-1] لم يتم العثور جديد ميت [6,-1] يتم إنشاؤه ، [5,-1] لم يولد لأنه ليس لديها ما يكفي من جيران الحي.

أعتقد أنني قد حل المشكلة من قبل بفارغ الصبر إعادة ملء shadowCellHash في بداية كل World.tick

JSFiddle

  // This hash goes out of scope after each tick allowing any dead shadowCells to be garbage collected
  if (!this.settings.wrap) {
    var shadowCellHash = {};
    for (var i = 0; i < this.lifeList.length; i++) {
        var cell = this.lifeList[i];
        if (cell.key) {
          shadowCellHash[cell.key] = cell;
        }
    }
  }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top